利用Python进行数据分析-Pandas(第二部分)
上一个章节中我们主要是介绍了pandas两种数据类型的具体属性,这个章节主要介绍操作Series和DataFrame中的数据的基本手段。
一、基本功能
1、重新索引
pandas对象的一个重要方法是reindex,其作用是创建一个新对象,它的数据符合新的索引:
import pandas as pd obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c'])
print(obj)
d 4.5
b 7.2
a -5.3
c 3.6
dtype: float64
用该Series的reindex将会根据新索引进行重排。如果某个索引值当前不存在,就引入缺失值:
obj2 = obj.reindex(['a', 'b', 'c', 'd', 'e'])
print(obj2)
a -5.3
b 7.2
c 3.6
d 4.5
e NaN
dtype: float64
对于时间序列这样的有序数据,从新索引时可能需要做一些插值处理。method选项即可达到此目的,例如,使用fill可以实现前向值填充:
obj3 = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
print(obj3)
0 blue
2 purple
4 yellow
dtype: object
print(obj3.reindex(range(6), method='ffill'))
0 blue
1 blue
2 purple
3 purple
4 yellow
5 yellow
dtype: object
借助DataFrame,reindex可以修改(行)索引和列。只传递一个序列时,会重新索引结果的行:
frame = pd.DataFrame(np.arange(9).reshape(3, 3), index=['a', 'c', 'd'], columns=['Ohio', 'Texas', 'California'])
print(frame)
Ohio Texas California
a 0 1 2
c 3 4 5
d 6 7 8
frame2= frame.reindex(['a', 'b', 'c', 'd'])
print(frame2)
Ohio Texas California
a 0.0 1.0 2.0
b NaN NaN NaN
c 3.0 4.0 5.0
d 6.0 7.0 8.0
列可以用columns关键字重新索引:
states = ['Texas', 'Utah', 'California']
print(frame.reindex(columns=states))
Texas Utah California
a 1 NaN 2
c 4 NaN 5
d 7 NaN 8
如下列出了reindex函数的各参数及说明:
- index 用作索引的新序列。既可以是index实例,也可以是其他序列类型的Python数据结构。Index会被完全使用,就像没有任何复制一样;
- method 插值(填充)方式;
- fill_value 在重新索引的过程中,需要引入缺失值时使用的替代值;
- limit 前向或后向填充时的最大填充量;
- tolerance 向前后向后填充时,填充不准确匹配项的最大间距(绝对值距离)
- level 在MultiIndex的指定级别上匹配简单索引,否则选取其子集;
- copy 默认为True,无论如何都复制;如果为False,则新旧相等就不复制。
2、丢弃指定轴上的项
丢弃某条轴上的一个或多个项很简单,只要有一个索引数组或列表即可。由于需要执行一些数据整理和集合逻辑,所以drop方法返回的是一个在指定轴上删除了指定值的新对象:
import pandas as pd
import numpy as np obj = pd.Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e'])
print(obj)
a 0.0
b 1.0
c 2.0
d 3.0
e 4.0
dtype: float64
删除索引C,并生成新的对象:
new_obj = obj.drop('c')
print(new_obj)
a 0.0
b 1.0
d 3.0
e 4.0
dtype: float64
删除列表索引,并生成新的对象:
print(obj.drop(['d', 'c']))
a 0.0
b 1.0
e 4.0
dtype: float64
对于DataFrame,可以删除任意轴上的索引值。为了演示,先新建一个DataFrame栗子:
import pandas as pd
import numpy as np
data = pd.DataFrame(np.arange(16).reshape(4, 4), index=['Ohio', 'Colorado', 'Utah', 'NewYork'], columns=['one', 'two', 'three', 'four'])
print(data)
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
Utah 8 9 10 11
NewYork 12 13 14 15
用标签序列调用drop会从行标签(axis 0)删除值:
print(data.drop(['Colorado', 'Ohio']))
one two three four
Utah 8 9 10 11
NewYork 12 13 14 15
通过传递axis=1或axis='columns'可以删除列的值:
print(data.drop('two', axis=1))
one three four
Ohio 0 2 3
Colorado 4 6 7
Utah 8 10 11
NewYork 12 14 15
print(data.drop(['two', 'four'], axis='columns'))
one three
Ohio 0 2
Colorado 4 6
Utah 8 10
NewYork 12 14
许多函数,如drop,会修改Series或DataFrame的大小和形状,可以就地修改对象,不会返回新的对象:
obj.drop('c', inplace=True)
print(obj)
a 0.0
b 1.0
d 3.0
e 4.0
dtype: float64
因此小心使用inplace,它会销毁所有被删除的数据。
3、索引、选取和过滤
Series索引(obj[...])的工作方式类似于NumPy数组的索引,只不过Series的索引值不只是整数。下面是几个例子:
import pandas as pd
import numpy as np
obj = pd.Series(np.arange(4.), index=['a', 'b', 'c', 'd'])
print(obj)
a 0.0
b 1.0
c 2.0
d 3.0
dtype: float64
获得索引为b的数据:
print(obj['b'])
1.0
获得自动索引为1的数据:
print(obj[1])
1.0
获得自动索引列表[2:4]的数据:
print(obj[2:4])
c 2.0
d 3.0
dtype: float64
也可以使用自定义索引:
print(obj[['b', 'a', 'd']])
b 1.0
a 0.0
d 3.0
dtype: float64
使用自动索引:
print(obj[[1, 3]])
b 1.0
d 3.0
dtype: float64
可以使用判断切片:
print(obj[obj < 2])
a 0.0
b 1.0
dtype: float64
利用标签的切片运算与普通的Python切片运算不同,其末端是包含的:
print(obj['b':'d'])
b 1.0
c 2.0
d 3.0
dtype: float64
用切片可以对Series的相应部分进行设置:
obj['b':'c'] = 5
print(obj)
a 0.0
b 5.0
c 5.0
d 3.0
dtype: float64
用一个值或序列对DataFrame进行索引其实就是获取一个或多个列:
data = pd.DataFrame(np.arange(16).reshape(4, 4), index=['Ohio', 'Colorado', 'Utah', 'NewYork'], columns=['one', 'two', 'three', 'four'])
print(data)
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
Utah 8 9 10 11
NewYork 12 13 14 15
使用索引举例:
print(data['two'])
Ohio 1
Colorado 5
Utah 9
NewYork 13
Name: two, dtype: int32
print(data[['three', 'one']])
three one
Ohio 2 0
Colorado 6 4
Utah 10 8
NewYork 14 12
这种索引方式有几个特殊的情况。首先通过切片或布尔型数组选取数据:
print(data[:2])
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
print(data[data['three'] > 5])
one two three four
Colorado 4 5 6 7
Utah 8 9 10 11
NewYork 12 13 14 15
选取行的语法data[:2]十分方便。向[ ]传递单一的元素或列表,就可选择列。另一种用法是通过布尔型DataFrame(比如下面这个由标量比较运算得出的)进行索引:
print(data < 5)
data[data < 5] = 0
print(data)
one two three four
Ohio True True True True
Colorado True False False False
Utah False False False False
NewYork False False False False
one two three four
Ohio 0 0 0 0
Colorado 0 5 6 7
Utah 8 9 10 11
NewYork 12 13 14 15
这使得DataFrame的语法与NumPy二维数组的语法很像。
4、用loc和iloc进行选取
对于DataFrame的行的标签索引,引入了特殊的标签运算符loc和iloc。它们可以让你用类似NumPy的标记,使用轴标签(loc)或整数索引(iloc),从DataFrame选择行和列的子集。
作为一个初步示例,让我们通过标签选择一行和多列。
print(data.loc['Colorado', ['two', 'three']])
two 5
three 6
Name: Colorado, dtype: int32
然后用iloc和整数进行行选取:
print(data.iloc[2, [3, 0, 1]])
four 11
one 8
two 9
Name: Utah, dtype: int32
print(data.iloc[2])
one 8
two 9
three 10
four 11
Name: Utah, dtype: int32
print(data.iloc[[1, 2], [3, 0, 1]])
four one two
Colorado 7 4 5
Utah 11 8 9
这两个索引函数也适用于一个标签或多个标签的切片:
print(data.loc[:'Utah', 'two'])
Ohio 1
Colorado 5
Utah 9
Name: two, dtype: int32
print(data.iloc[:, :3][data.three > 5])
one two three
Colorado 4 5 6
Utah 8 9 10
NewYork 12 13 14
所以,在pandas中,有多个方法可以选取和重新组合数据。
5、整数索引
处理整数索引的pandas对象常常难住新手,因为它与Python内置的列表和元组的索引语法不同。例如,你可能不认为下面的代码会出处:
ser = pd.Series(np.arange(3.))
print(ser[-1])
return self._engine.get_value(s, k, tz=getattr(series.dtype, "tz", None))
File "pandas\_libs\index.pyx", line 80, in pandas._libs.index.IndexEngine.get_value
File "pandas\_libs\index.pyx", line 88, in pandas._libs.index.IndexEngine.get_value
File "pandas\_libs\index.pyx", line 131, in pandas._libs.index.IndexEngine.get_loc
File "pandas\_libs\hashtable_class_helper.pxi", line 992, in pandas._libs.hashtable.Int64HashTable.get_item
File "pandas\_libs\hashtable_class_helper.pxi", line 998, in pandas._libs.hashtable.Int64HashTable.get_item
KeyError: -1
以上执行会报错。已经自动产生了0开头的索引,引用-1时则报错!
但是对于非整数索引,则不会产生歧义:
ser2 = pd.Series(np.arange(3.), index=['a', 'b', 'c'])
print(ser2[-1])
2.0
为了进行统一,如果轴索引含有整数,数据选取总会使用标签。为了更准确,请使用loc(标签)或iloc(整数):
ser = pd.Series(np.arange(3.))
print(ser[:1])
0 0.0
dtype: float64
print(ser.loc[:1])
print(ser.iloc[:1])
0 0.0
1 1.0
dtype: float64
0 0.0
dtype: float64
6、算术运算和数据对齐
pandas最重要的一个功能是,它可以对不同索引的对象进行算术运算。在将对象相加时,如果存在不同的索引对,则结果的索引就是该索引对的并集。对于有数据库经验的用户,这就像在索引标签上进行自动外连接:
s1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=['a', 'c', 'd', 'e'])
s2 = pd.Series([-2.1, 3.6, -1.5, 4, 3.1], index=['a', 'c', 'e', 'f', 'g'])
print(s1)
print(s2)
a 7.3
c -2.5
d 3.4
e 1.5
dtype: float64
a -2.1
c 3.6
e -1.5
f 4.0
g 3.1
dtype: float64
将它们相加就会产生:
print(s1+s2)
a 5.2
c 1.1
d NaN
e 0.0
f NaN
g NaN
dtype: float64
自动的数据对齐操作在不重叠的索引处引入了NA值。缺失值会在算术运算过程中传播。
对于DataFrame,对齐操作会同时发生在行和列上:
df1 = pd.DataFrame(np.arange(9.).reshape(3, 3), columns=list('bcd'), index=['Ohio', 'Texas', 'Colorado'])
df2 = pd.DataFrame(np.arange(12.).reshape(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])
print(df1)
print(df2)
b c d
Ohio 0.0 1.0 2.0
Texas 3.0 4.0 5.0
Colorado 6.0 7.0 8.0
b d e
Utah 0.0 1.0 2.0
Ohio 3.0 4.0 5.0
Texas 6.0 7.0 8.0
Oregon 9.0 10.0 11.0
把它们相加后将会返回一个新的DataFrame,其索引和列为原来那两个DataFrame的并集:
print(df1+df2)
b c d e
Colorado NaN NaN NaN NaN
Ohio 3.0 NaN 6.0 NaN
Oregon NaN NaN NaN NaN
Texas 9.0 NaN 12.0 NaN
Utah NaN NaN NaN NaN
因为'c'和'e'列均不在两个DataFrame对象中,在结果中以缺省值呈现。行也是同样。
7、在算术方法中填充值
在对不同索引的对象进行算术运算时,你可能希望当一个对象中某个轴标签在另一个对象中找不到时填充一个特殊值(比如0):
df1 = pd.DataFrame(np.arange(12).reshape((3, 4)), columns=list('abcd'))
df2 = pd.DataFrame(np.arange(20).reshape((4, 5)), columns=list('abcde'))
df2.loc[1, 'b'] = np.nan
print(df1)
print(df2)
a b c d
0 0 1 2 3
1 4 5 6 7
2 8 9 10 11
a b c d e
0 0 1.0 2 3 4
1 5 NaN 7 8 9
2 10 11.0 12 13 14
3 15 16.0 17 18 19
将它们相加时,没有重叠的位置就会产生NA值:
print(df1+df2)
a b c d e
0 0.0 2.0 4.0 6.0 NaN
1 9.0 NaN 13.0 15.0 NaN
2 18.0 20.0 22.0 24.0 NaN
3 NaN NaN NaN NaN NaN
使用df1的add方法,传入df2以及一个fill_value参数:
print(df1.add(df2, fill_value=0))
a b c d e
0 0.0 2.0 4.0 6.0 4.0
1 9.0 5.0 13.0 15.0 9.0
2 18.0 20.0 22.0 24.0 14.0
3 15.0 16.0 17.0 18.0 19.0
如下列表列出了Series和DataFrame的算术方法。它们每个都有一个副本,以字母r开头,它会翻转参数。因此这两个语句是等价的:
print(1/df1)
a b c d
0 inf 1.000000 0.500000 0.333333
1 0.250 0.200000 0.166667 0.142857
2 0.125 0.111111 0.100000 0.090909
print(df1.rdiv(1))
a b c d
0 inf 1.000000 0.500000 0.333333
1 0.250 0.200000 0.166667 0.142857
2 0.125 0.111111 0.100000 0.090909方法
方法 | 说明 |
add,radd | 用于加法(+)的方法 |
sub,rsub | 用于减法(—)的方法 |
div,rdiv | 用于除法(/)的方法 |
floordiv,rfloordiv | 用于底除(//)的方法 |
mul,rmul | 用于乘法(*)的方法 |
pow,rpow | 用于指数(**)的方法 |
与此类似,在对Series或DataFrame重新索引时,也可以指定一个填充值:
print(df1.reindex(columns=df2.columns, fill_value=0))
a b c d e
0 0 1 2 3 0
1 4 5 6 7 0
2 8 9 10 11 0
8、DataFrame和Series之间的运算
跟不同维度的NumPy数组一样,DataFrame和Series之间算术运算也是有明确规定的。先看一个栗子,计算一个二维数组与其某行之间的差:
arr = np.arange(12.).reshape((3, 4))
print(arr)
[[ 0. 1. 2. 3.]
[ 4. 5. 6. 7.]
[ 8. 9. 10. 11.]]
print(arr-arr[0])
[[0. 0. 0. 0.]
[4. 4. 4. 4.]
[8. 8. 8. 8.]]
当我们从arr减去arr[0],每一行都会执行这个操作。这就叫做广播(broadcasting)。DataFrame和Series之间的运算差不多也是如此:
frame = pd.DataFrame(np.arange(12.).reshape(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])
series = frame.iloc[0]
print(frame)
print(series)
b d e
Utah 0.0 1.0 2.0
Ohio 3.0 4.0 5.0
Texas 6.0 7.0 8.0
Oregon 9.0 10.0 11.0
b 0.0
d 1.0
e 2.0
Name: Utah, dtype: float64
默认情况下,DataFrame和Series之间的算术运算会将series的索引匹配到DataFrame的列,然后沿着行一直向下广播:
print(frame-series)
b d e
Utah 0.0 0.0 0.0
Ohio 3.0 3.0 3.0
Texas 6.0 6.0 6.0
Oregon 9.0 9.0 9.0
如果某个索引值在DataFrame的列或Series的索引中找不到,则参与运算的两个对象就会被重新索引以形成并集:
series2 = pd.Series(range(3), index=['b', 'e', 'f'])
print(frame+series2)
b d e f
Utah 0.0 NaN 3.0 NaN
Ohio 3.0 NaN 6.0 NaN
Texas 6.0 NaN 9.0 NaN
Oregon 9.0 NaN 12.0 NaN
如果希望匹配行且在列上广播,则必须使用算术运算方法。
series3 = frame['d']
print(series3)
print(frame.sub(series3, axis='index'))
Utah 1.0
Ohio 4.0
Texas 7.0
Oregon 10.0
Name: d, dtype: float64
b d e
Utah -1.0 0.0 1.0
Ohio -1.0 0.0 1.0
Texas -1.0 0.0 1.0
Oregon -1.0 0.0 1.0
传入的轴号就是希望匹配的轴。在本例中,我们的目的是匹配DataFrame的行索引(axis=‘index’ or axis=0)并进行广播。
9、函数应用和映射
NumPy的ufuncs(元素级数组方法)也可以用于操作pandas对象:
frame = pd.DataFrame(np.random.rand(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])
print(frame)
print(np.abs(frame))
b d e
Utah 0.064023 0.692445 0.397648
Ohio 0.307545 0.700493 0.054521
Texas 0.790282 0.857526 0.176732
Oregon 0.845433 0.030763 0.197624
b d e
Utah 0.064023 0.692445 0.397648
Ohio 0.307545 0.700493 0.054521
Texas 0.790282 0.857526 0.176732
Oregon 0.845433 0.030763 0.197624
另一个常见的操作是,将函数应用到由各列或行所形成的一维数组上。DataFrame的apply方法即可实现此功能:
f = lambda x: x.max() - x.min()
print(frame.apply(f))
b 0.628426
d 0.228863
e 0.327750
dtype: float64
这里的函数f,计算了一个Series的最大值和最小值的差,在frame的每列都执行了一次。结果是一个series,使用frame的列作为索引。
如果传递axis='columns'到apply,这个函数会在每行执行:
print(frame.apply(f, axis=1))
Utah 0.632225
Ohio 0.165264
Texas 0.829253
Oregon 0.035064
dtype: float64
许多最为常见的数组统计功能都被实现成DataFrame的方法(如sum和mean),因此无需使用apply方法。
传递到apply的函数不是必须返回一个标量,还可以返回由多个值祖晨给的Series:
def f(x):
return pd.Series([x.min(), x.max()], index=['min', 'max']) print(frame.apply(f))
b d e
min 0.126930 0.002095 0.107104
max 0.847219 0.816830 0.932785
元素级的Python函数也是可以用的。假如你想得到frame中各个浮点值的格式字符串,使用applymap即可:
print(frame.applymap(format))
b d e
Utah 0.6798017353255632 0.597186588130127 0.19187846531150032
Ohio 0.5847604949858233 0.5343443064790626 0.7629954378984624
Texas 0.16706070519049687 0.29479050054459943 0.6530392097716237
Oregon 0.8484367833746287 0.5218312157057384 0.021621564616659317
之所以叫做applymap,是因为Series有一个用于应用元素级函数的map方法:
print(frame['e'].map(format))
Utah 0.4383346697291973
Ohio 0.7597781582580012
Texas 0.011837912674436457
Oregon 0.4294773649163831
Name: e, dtype: object
说明:因为是随机函数,因此产生的值有可能不一样,如要一致,请定义随机种子数。
10、排序和排名
根据条件对数据集排序(sorting)也是一种重要的内置运算。要对行或对列索引进行排序(按字典顺序),可使用sort_index方法,它将返回一个已排序的新对象:
obj = pd.Series(range(4), index=['d', 'a', 'b', 'c'])
print(obj.sort_index())
a 1
b 2
c 3
d 0
dtype: int64
对于DataFrame,则可以根据任意一个轴上的索引j进行排序:
frame = pd.DataFrame(np.arange(8.).reshape((2, 4)), index=['three', 'one'], columns=['d', 'a', 'b', 'c'])
print(frame.sort_index())
d a b c
one 4.0 5.0 6.0 7.0
three 0.0 1.0 2.0 3.0
print(frame.sort_index(axis=1))
a b c d
three 1.0 2.0 3.0 0.0
one 5.0 6.0 7.0 4.0
数据默认是按升序排序的,但也可以是降序排序:
print(frame.sort_index(axis=1, ascending=False))
d c b a
three 0.0 3.0 2.0 1.0
one 4.0 7.0 6.0 5.0
如要按值对Series进行排序,可使用其sort_values方法:
obj = pd.Series([4, 7, -3, 2])
print(obj.sort_values())
2 -3
3 2
0 4
1 7
dtype: int64
在排序时,任何缺失值默认都会被放到Series的末尾:
obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
print(obj.sort_values())
4 -3.0
5 2.0
0 4.0
2 7.0
1 NaN
3 NaN
dtype: float64
当排序一个DataFrame时,你可能希望根据一个或多个列中的值进行排序。将一个或多个列的名字传递给sort_values的by选项即可达到该目的:
frame = pd.DataFrame({'b':[4, 7, -3, 2], 'a':[0, 1, 0, 1]})
print(frame)
b a
0 4 0
1 7 1
2 -3 0
3 2 1
print(frame.sort_values(by='b'))
b a
2 -3 0
3 2 1
0 4 0
1 7 1
要根据多个列进行排序,传入名称的列表即可:
print(frame.sort_values(by=['a', 'b']))
b a
2 -3 0
0 4 0
3 2 1
1 7 1
排名会从1开始一直到数组中有效数据的数量。接下来介绍Series和DataFrame的rank方法。默认情况下,rank是通过“为各组分配一个平均排名”的方式破坏平级关系的:
obj = pd.Series([7, -5, 7, 4, 2, 0, 4])
print(obj.rank())
0 6.5
1 1.0
2 6.5
3 4.5
4 3.0
5 2.0
6 4.5
dtype: float64
也可以根据值在原始数据中出现的顺序给出排名:
print(obj.rank(method='first'))
0 6.0
1 1.0
2 7.0
3 4.0
4 3.0
5 2.0
6 5.0
dtype: float64
这里,条目0和2没有使用平均排名6.5,它们被设成了6和7,因为数据中标签0位于标签2的前面。
也可以按降序进行排名:
print(obj.rank(ascending=False, method='max'))
0 2.0
1 7.0
2 2.0
3 4.0
4 5.0
5 6.0
6 4.0
dtype: float64
如下表列出了所有用于破坏平级关系的method选项。DataFrame可以在行或列上计算排名:
frame = pd.DataFrame({'b': [4.3, 7, -3, 2], 'a': [0, 1, 0, 1], 'c': [-2, 5, 8, -2.5]})
print(frame)
print(frame.rank(axis='columns'))
b a c
0 4.3 0 -2.0
1 7.0 1 5.0
2 -3.0 0 8.0
3 2.0 1 -2.5
b a c
0 3.0 2.0 1.0
1 3.0 1.0 2.0
2 1.0 2.0 3.0
3 3.0 2.0 1.0
方法 | 说明 |
average | 默认:在相等分组中,为各个值分配平均排名 |
min | 使用整个分组的最小排名 |
max | 使用整个分组的最大排名 |
first | 按值在原始数据中的出现顺序分配排名 |
dense | 类似于min方法,但是排名总是在组间增加1,而不是组中相同的元素数 |
11、带有重复标签的轴索引
直到目前为止,所介绍的所有栗子都有着唯一的轴标签(索引值)。虽然许多pandas函数(如reindex)都要求标签唯一,但这并不是强制性的。我们来看看下面这个简单的带有重复索引值的Series:
obj = pd.Series(range(5), index=['a', 'a', 'b', 'b', 'c'])
print(obj)
a 0
a 1
b 2
b 3
c 4
dtype: int64
索引的is_unique属性可以告诉你它的值是否是唯一的:
print(obj.index.is_unique)
False
对于带有重复值的索引,数据选取的行为将会有些不同。如果某个索引对应多个值,则返回一个Series;而对应单个值的,则返回一个标量值:
print(obj['a'])
a 0
a 1
dtype: int64
print(obj['c'])
4
这样会使代码边复杂,因为索引的输出类型会根据标签是否有重复发生变化。
对DataFrame的行进行索引时也是如此:
df = pd.DataFrame(np.random.randn(4, 3), index=['a', 'a', 'b', 'b'])
print(df)
print(df.loc['b'])
0 1 2
a 1.256824 1.291061 -0.685927
a -0.407044 -0.353979 -1.864022
b 0.037937 0.517678 0.680367
b 3.037344 -0.065109 0.588987
0 1 2
b 0.037937 0.517678 0.680367
b 3.037344 -0.065109 0.588987
二、汇总和计算描述统计
12、汇总和计算描述统计
pandas对象拥有一组常用的数学和统计方法。它们大部分都属于约简和汇总统计,用于从Series中提取单个值(如sum或mean)或从DataFrame的行或列中提取一个Series。跟对应的NumPy数组方法相比,它们都是基于没有缺失数据的假设而构建的。
df = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]], index=['a', 'b', 'c', 'd'], columns=['one', 'two'])
print(df)
one two
a 1.40 NaN
b 7.10 -4.5
c NaN NaN
d 0.75 -1.3
调用DataFrame的sum方法将返回一个含有列的和的Series:
print(df.sum())
one 9.25
two -5.80
dtype: float64
传入axis='columns'或axis=1将会按行进行求和运算:
print(df.sum(axis=1))
a 1.40
b 2.60
c 0.00
d -0.55
dtype: float64
NA值会自动被排除,除非整个切片(这里指的是行或列)都是NA。通过skipna选项可以禁用该功能:
print(df.mean(axis=1, skipna=False))
a NaN
b 1.300
c NaN
d -0.275
dtype: float64
如下列出了这些约简方法的常用选项:
选项 | 说明 |
axis | 约简的轴。由DataFrame的行用0,列用1 |
skipna | 排除缺失值,默认值为True |
level | 如果轴是层次化索引的(即MultiIndex),则根据level分组约简 |
有些方法(如idxmin和idxmax)返回的是间接统计(比如达到最小值或最大值的索引):
print(df.idxmax())
one b
two d
dtype: object
另一些方法则是累计型的:
print(df.cumsum())
one two
a 1.40 NaN
b 8.50 -4.5
c NaN NaN
d 9.25 -5.8
还有一种方法,它就不是约简型也不是累计型。describe就是一个栗子,它用于一次性产生多个汇总统计:
print(df.describe())
one two
count 3.000000 2.000000
mean 3.083333 -2.900000
std 3.493685 2.262742
min 0.750000 -4.500000
25% 1.075000 -3.700000
50% 1.400000 -2.900000
75% 4.250000 -2.100000
max 7.100000 -1.300000
对于非数值型数据,describe会产生另外一种汇总统计:
obj = pd.Series(['a', 'a', 'b', 'c']*4)
print(obj.describe())
count 16
unique 3
top a
freq 8
dtype: object
如下列表列出了所有与描述统计相关的方法:
表:描述和汇总统计
方法 | 说明 |
count | 非NA值的数量 |
describe | 针对Series或各DataFrame列计算汇总统计 |
min、max | 计算最小值和最大值 |
argmin、argmax | 计算能够获取到最小值和最大值的索引位置(整数) |
idxmin、idxmax | 计算能够获取到最小值和最大值的索引值 |
quantile | 计算样本的分位数(0到1) |
sum | 值的总和 |
mean | 值的平均数 |
median | 值的算术中位数(50%分位数) |
mad | 根据平均值计算平均绝对离差 |
var | 样本值的方差 |
std | 样本值的标准差 |
skew | 样本值的偏度(三阶矩) |
kurt | 样本值的峰度(四阶矩) |
cumsum | 样本值的累计和 |
cummin、cummax | 样本值的累计最大值和累计最小值 |
cumprod | 样本值的累计积 |
diff | 计算一阶差分(对时间序列很有用) |
pct_change | 计算百分数变化 |
13、相关系数与协方差
有些汇总统计(如相关系数和协方差)是通过参数对计算出来的。我们来看几个DataFrame,它们的数据来自Yahoo!Finance的股票价格和成交量,使用的是pandas-datareader包(可以用conda或pip安装):
import pandas_datareader.data as web all_data = {ticker: web.get_data_yahoo(ticker) for ticker in ['AAPL', 'IBM', 'MSFT', 'GOOG']}
price = pd.DataFrame({ticker: data['Adj Close'] for ticker, data in all_data.items()})
volume = pd.DataFrame({ticker: data['Volume'] for ticker, data in all_data.items()})
returns = price.pct_change()
print(returns.tail())
AAPL IBM MSFT GOOG
Date
2019-11-22 -0.000878 0.003736 0.000736 -0.004618
2019-11-25 0.017534 0.012133 0.010963 0.008762
2019-11-26 -0.007809 -0.006472 0.005290 0.005250
2019-11-27 0.013432 -0.009771 0.001908 -0.000426
2019-11-29 -0.002203 0.005083 -0.006171 -0.006116
Series的corr方法用于计算两个Series中重叠的、非NA的、按索引对齐的值的相关系数。与此类似,cov用于计算协方差:
print(returns['MSFT'].corr(returns['IBM']))
0.48748863221002536
print(returns['MSFT'].cov(returns['IBM']))
9.403469349907563e-05
因为MSFT是一个合理的Python属性,我们还可以用更简洁的语法选择列:
print(returns.MSFT.corr(returns.IBM))
0.48748863221002536
另一方面,DataFrame的corr和cov方法将以DataFrame的形式返回完整的相关系数或协方差矩阵:
相关系数:
print(returns.corr())
AAPL IBM MSFT GOOG
AAPL 1.000000 0.406404 0.575217 0.523380
IBM 0.406404 1.000000 0.487489 0.414135
MSFT 0.575217 0.487489 1.000000 0.660486
GOOG 0.523380 0.414135 0.660486 1.000000
协方差矩阵:
print(returns.cov())
AAPL IBM MSFT GOOG
AAPL 0.000246 0.000083 0.000133 0.000125
IBM 0.000083 0.000170 0.000094 0.000082
MSFT 0.000133 0.000094 0.000218 0.000148
GOOG 0.000125 0.000082 0.000148 0.000231
利用DataFrame的corrwith方法,你可以计算其列或行跟另一个Series或DataFrame之间的相关系数。传入一个Series将会返回一个相关系数值Series(针对各列进行计算):
print(returns.corrwith(returns.IBM))
AAPL 0.406404
IBM 1.000000
MSFT 0.487489
GOOG 0.414135
dtype: float64
传入一个DataFrame则会计算按列名配对的相关系数。这里,计算百分比变化与成交量的相关系数:
print(returns.corrwith(volume))
AAPL -0.119077
IBM -0.133046
MSFT -0.085162
GOOG -0.005952
dtype: float64
传入axis='columns'即可按行进行计算。无论如何,在计算相关系数之前,所有的数据项都会按标签对齐。
14、唯一值、值计数以及成员资格
还有一类方法可以从一维Series的值中抽取信息。
obj = pd.Series(['c', 'a', 'd', 'a', 'a', 'b', 'b', 'c', 'c'])
uniques = obj.unique()
print(uniques)
['c' 'a' 'd' 'b']
返回的唯一值是未排序的,如果需要的话,可以对结果再次进行排序(uniques.sort())。相似的,value_counts用于计算一个Series中各值出现的频率:
print(obj.value_counts())
a 3
c 3
b 2
d 1
dtype: int64
为了便于查看,结果Series是按值频率降序排列的。value_counts还是一个顶级pandas方法,可用于任何数组或序列:
print(pd.value_counts(obj.values, sort=False))
d 1
c 3
b 2
a 3
dtype: int64
isin用于判断矢量集合的成员资格,可用于过滤Series中或DataFrame列中数据的子集:
mask = obj.isin(['b', 'c'])
print(mask)
0 True
1 False
2 False
3 False
4 False
5 True
6 True
7 True
8 True
dtype: bool
与isin类似的是Index.get_indexer方法,它可以给你一个索引数组,从可能包含重复值的数组到另一个不同值的数组:
print(pd.Index(unique_vals).get_indexer(to_match))
[0 2 1 1 0 2]
如下表给出了这几个方法的一些参考信息:
方法 | 说明 |
isin | 计算一个表示'Series各值是否包含于传入的值序列中'的布尔型数组 |
match | 计算一个数组中的各值到另一个不同值数组的整数索引;对于数据对齐和连接类型的操作十分有用 |
unique | 计算Series中的唯一值数组,按发现的顺序返回 |
value_counts | 返回一个Series,其索引为唯一值,其值为频率,按计数值降序排列 |
有时,你可能希望得到DataFrame中多个相关列的一张柱状图。例如:
data = pd.DataFrame({'Qul': [1, 3, 4, 3, 4], 'Qu2': [2, 3, 1, 2, 3], 'Qu3': [1, 5, 2, 4, 4]})
print(data)
Qul Qu2 Qu3
0 1 2 1
1 3 3 5
2 4 1 2
3 3 2 4
4 4 3 4
将pandas.value_counts传给该DataFrame的apply函数,就会出现:
result = data.apply(pd.value_counts).fillna(0)
print(result)
Qul Qu2 Qu3
1 1.0 1.0 1.0
2 0.0 2.0 1.0
3 2.0 2.0 0.0
4 2.0 0.0 2.0
5 0.0 0.0 1.0
这里,结果中的行标签是所有列的唯一值。后面的频率是每个列中这些值的相应计数。
利用Python进行数据分析-Pandas(第二部分)的更多相关文章
- 利用Python进行数据分析——pandas入门
利用Python进行数据分析--pandas入门 基于NumPy建立的 from pandas importSeries,DataFrame,import pandas as pd 一.两种数据结构 ...
- 利用Python进行数据分析-Pandas(第一部分)
利用Python进行数据分析-Pandas: 在Pandas库中最重要的两个数据类型,分别是Series和DataFrame.如下的内容主要围绕这两个方面展开叙述! 在进行数据分析时,我们知道有两个基 ...
- 利用Python进行数据分析 2017 第二版 项目代码
最近在学习<利用Python进行数据分析>,找到了github项目的地址, 英文版本,中文版本 (非常感谢翻译中文的作者). mark一下,方便后边学习查找.
- 利用Python进行数据分析-Pandas(第四部分-数据清洗和准备)
在数据分析和建模的过程中,相当多的时间要用在数据准备上:加载.清理.转换以及重塑上.这些工作会占到分析时间的80%或更多.有时,存储在文件和数据库中的数据的格式不适合某个特定的任务.研究者都选择使用编 ...
- 利用Python进行数据分析-Pandas(第六部分-数据聚合与分组运算)
对数据集进行分组并对各组应用一个函数(无论是聚合还是转换),通常是数据分析工作中的重要环节.在将数据集加载.融合.准备好之后,通常是计算分组统计或生成透视表.pandas提供了一个灵活高效的group ...
- 利用Python进行数据分析-Pandas(第七部分-时间序列)
时间序列(time series)数据是一种重要的结构化数据形式,应用于多个领域,包括金融学.经济学.生态学.神经科学.物理学等.时间序列数据的意义取决于具体的应用场景,主要有以下几种: 时间戳(ti ...
- 利用Python进行数据分析-Pandas(第五部分-数据规整:聚合、合并和重塑)
在许多应用中,数据可能分散在许多文件或数据库中,存储的形式也不利于分析.本部分关注可以聚合.合并.重塑数据的方法. 1.层次化索引 层次化索引(hierarchical indexing)是panda ...
- 利用Python进行数据分析-Pandas(第三部分)
访问数据是使用本书所介绍的这些工具的第一步.这里会着重介绍pandas的数据输入与输出,虽然别的库中也有不少以此为目的的工具. 输入输出通常可以划分为几个大类:读取文本文件和其他更高效的磁盘存储格式, ...
- 利用python进行数据分析--pandas入门2
随书练习,第五章 pandas入门2 # coding: utf-8 # In[1]: from pandas import Series,DataFrame import pandas as pd ...
随机推荐
- docker进阶之路-基础篇 | 一:环境搭建
转载请注明作者及出处: 作者:银河架构师 原文链接:https://www.cnblogs.com/luas/p/12061747.html 一.准备工作 查看内核 Docker 要求 CentOS ...
- 注解slf4j的日志该用哪个级别
slf4j的日志级别分为五种 info.debug.error.warn.trane 常用的是这是三个. info 一般处理业务逻辑的时候使用,就跟 system.err打印一样,用 ...
- docker-compose编排参数详解
一.前言 Compose是一个用于定义和运行多容器Docker应用程序的工具.使用Compose,您可以使用YAML文件来配置应用程序的服务.然后,使用单个命令,您可以从配置中创建并启动所有服务. C ...
- Soc常见问题
SOC常见问题解答 1.SOC FPGA中的ARM是软核还是硬核?ARM核的外设是软核还是硬核? SOC FPGA 中的ARM核是硬核.所以简称HPS,Hardware Processor Syste ...
- 建议2:注意Javascript数据类型的特殊性---(2)慎用JavaScript类型自动转换
在JavaScript中能够自动转换变量的数据类型,这种转换是一种隐性行为.在自动转换数据类型时,JavaScript一般遵循:如果某个类型的值被用于需要其它类型的值的环境中,JavaScript就自 ...
- java设计模式(二)单例模式,一生只爱一人,只争一朝一夕
单例模式:保证一个类在内存中的对象唯一,有且仅能实例化一次.(如多个代码块需要读取配置文件,or开启事务,orjdbc读取数据源就是个经典例子)参考:吟啸且徐行 实现步骤: 私有构造方法.保证唯一的 ...
- EF Core 基础知识
数据库连接字符串 在 ASP.NET Core 添加配置片段: { "ConnectionStrings": { "BloggingDatabase": &qu ...
- 原生JS实现移动端轮播图
功能描述: 自动无缝轮播图片,底部小圆点跟图片保持一致:手指左右移动轮播图,移动距离大于50px播放下一张(或上一张),小于50px则回弹 具体功能实现: 1.定时器 自动轮播图片 先声明一个inde ...
- JAVA杂记
写在前面:期末考试要来了,对于一个暑假自学了几天JAVA之后一个学期划水的人来说现在就是踏上了JAVA预习之路.下面是自己xjb记的东西,主要是根据老师给的流程图总结的自己认为需要的一些零碎的小知识, ...
- 安全性测试:OWASP ZAP 2.8 使用指南(四):ZAP扫描移动应用安全
在做移动应用(APP,小程序等)测试时,需要关注应用安全性. ZAP是可以用来进行手机移动应用渗透性测试扫描的. 正因为ZAP是采用“中间代理”的形式,截取并扫描所有客户端与服务器的交互请求,作为客户 ...