5.1 pandas 数据结构介绍
为了入门pandas,你需要熟悉两个常用的工具数据结构:series和dataframe。
5.1 Series
Series是一种一维的数组型对象,它包含了一个值序列(与Numpy中的类型相似),并且包含了数据标签,称为索引(index)。最简单的序列可以仅仅由一个数组形成:
In [1]: import pandas as pd
In [2]: import numpy as np
In [3]: obj = pd.Series([4,7,-5,3])
In [4]: obj
Out[4]:
0 4
1 7
2 -5
3 3
dtype: int64
交互式环境中Series的字符串表示,索引在左边,值在右边。由于我们不为数据指定索引,默认生成的索引是从0到N-1(N是数据的长度)。你可以通过values属性和index属性分别获得Series对象的值和索引:
In [5]: obj.values
Out[5]: array([ 4, 7, -5, 3])
In [6]: obj.index # 类似于range(4)
Out[6]: RangeIndex(start=0, stop=4, step=1) # 开始是0,停止是4,步进为1
通常需要创建一个索引序列,用标签标识每个数据点:
In [7]: obj2 = pd.Series([4,7,-5,3],index=['d','v','a','c'])
In [8]: obj2
Out[8]:
d 4
v 7
a -5
c 3
dtype: int64
In [11]: obj2.index
Out[11]: Index(['d', 'v', 'a', 'c'], dtype='object')
与numpy的数组相比,你可以在从数据中选择数据的时候使用标签来进行索引:
In [14]: obj2['a']
Out[14]: -5
In [15]: obj2['v']
Out[15]: 7
In [16]: obj2[['c','a','d']]
Out[16]:
c 3
a -5
d 4
dtype: int64
上面的例子中,['c','a','d']包含的不是数字而是字符串,作为索引列表。
使用numpy的函数或者numpy风格的操作,比如使用布尔值数组进行过滤,与标量相乘,或是应用数学函数,这些操作将保存索引值连接:
In [17]: obj2[obj2>0]
Out[17]:
d 4
v 7
c 3
dtype: int64
In [18]: obj2 *2
Out[18]:
d 8
v 14
a -10
c 6
dtype: int64
In [19]: np.exp(obj2)
Out[19]:
d 54.598150
v 1096.633158
a 0.006738
c 20.085537
dtype: float64
从另一个角度考虑Series,可以认为它是一个长度固定且有序的字典,因为它将索引值和数据值按位置配对。在你可能会使用字典的上下文中,也可以使用series:
In [21]: 'a' in obj2
Out[21]: True
In [22]: 'e' in obj2
Out[22]: False
如果你已经有数据包含在Python字典中,你可以使用字典生成一个series:
In [23]: sdata = {'xiongwei':34,'yaowei':24,'tunwei':36}
In [24]: obj3 = pd.Series(sdata)
In [25]: obj3
Out[25]:
xiongwei 34
yaowei 24
tunwei 36
dtype: int64
当你把字典传递给Series构造函数时,产生的Series的索引将是排序好的字典键。你可以将字典键按照你所想要的顺序传递给构造函数,从而使生成的Series的索引顺序符合你的预期:
In [26]: states = ['tunwei','yaowei','xiongwei']
In [27]: obj4 = pd.Series(sdata,index = states)
In [28]: obj4
Out[28]:
tunwei 36
yaowei 24
xiongwei 34
dtype: int64
上面的例子中,sdata中的三个值被放置在正确的位置,如果输入的值没有出现在sdata的键中,它对应的值是NaN,这是pandas中标记缺失值或NA值的方式,它将被排除在结果对象外。
pandas中使用isnull和notnull函数来检查缺失数据:
In [31]: pd.isnull(obj4)
Out[31]:
tunwei False
yaowei False
xiongwei False
datui True
dtype: bool
In [32]: pd.notnull(obj4)
Out[32]:
tunwei True
yaowei True
xiongwei True
datui False
dtype: bool
Series对象自身和其索引都有name属性,这个特性与pandas其他重要功能集合在一起:
In [36]: obj4.name = 'pop'
In [37]: obj4.index.name = 'state'
In [38]: obj4
Out[38]:
state
tunwei 36.0
yaowei 24.0
xiongwei 34.0
datui NaN
Name: pop, dtype: float64
Series的索引可以通过按位置赋值的方式进行改变:
In [42]: obj = pd.Series([4,7,-5,3])
In [43]: obj
Out[43]:
0 4
1 7
2 -5
3 3
dtype: int64
In [44]: obj.index = ['a','b','c','d']
In [45]: obj
Out[45]:
a 4
b 7
c -5
d 3
dtype: int64
5.1.2 DataFrame
DataFrame表示的是矩阵的数据表,它包含已排序的列集合,每一列可以是不同的值类型(数值、字符串、布尔值等)。DataFrame既有行索引也有列索引,它可以被视为一个共享相同索引的Series的字典。在DataFrame中,数据被存储为一个以上的二维块,而不是列表、字典或其他一维数组的集合。
有多重方式可以构建DataFrame,其中最常用的方式是利用包含等长度列表或numpy数组的字典来形成dataframe:
In [46]: data = {'piaoxiaomin':['34','24','36'],'jinxuanya':['34','23','34'],'quanzhixian':['34','24','34']}
In [47]: frame = pd.DataFrame(data)
In [48]: frame
Out[48]:
piaoxiaomin jinxuanya quanzhixian
0 34 34 34
1 24 23 24
2 36 34 34
对于大型DataFrame,head方法将会只选出头部的五行:
In [49]: frame.head()
Out[49]:
piaoxiaomin jinxuanya quanzhixian
0 34 34 34
1 24 23 24
2 36 34 34
如果你指定了列的顺序,DataFrame的列将会按照指定顺序排列:
In [50]: pd.DataFrame(data,columns=['quanzhixian','jinxuanya','piaoxiaomin'])
Out[50]:
quanzhixian jinxuanya piaoxiaomin
0 34 34 34
1 24 23 24
2 34 34 36
如果你传的列不包含在字典中,将会在结果中出现缺失值:
In [51]: frame2 = pd.DataFrame(data,columns=['jinxuanya','piaoxiaomin','qiamzjoxoam','jinzhenxi'],index = ['xiongwei','yaowei','tu
...: nwei'])
In [52]: frame2
Out[52]:
jinxuanya piaoxiaomin qiamzjoxoam jinzhenxi
xiongwei 34 34 NaN NaN
yaowei 23 24 NaN NaN
tunwei 34 36 NaN NaN
DataFrame中的一列,可以按字典型标记或属性那样检索为Series:
In [53]: frame2['piaoxiaomin']
Out[53]:
xiongwei 34
yaowei 24
tunwei 36
Name: piaoxiaomin, dtype: object
In [55]: frame2.jinxuanya
Out[55]:
xiongwei 34
yaowei 23
tunwei 34
Name: jinxuanya, dtype: object
frame2[colunm]对于任意列名均有效,但是frame2.column只在列名是有效的Python变量名时有效。
请注意,返回的Series与原DataFrame有相同的索引,且Series的name属性也会被合理地设置。
行也可以通过位置或特殊属性loc进行选取:
In [56]: frame2.loc['xiongwei']
Out[56]:
jinxuanya 34
piaoxiaomin 34
qiamzjoxoam NaN
jinzhenxi NaN
Name: xiongwei, dtype: object
列的引用是可以修改的,例如,空的‘datui’列可以复制为标量值或值数组:
In [60]: frame2['datui'] = 'baihuahua'
In [61]: frame2
Out[61]:
jinxuanya piaoxiaomin qiamzjoxoam jinzhenxi datui
xiongwei 34 34 NaN NaN baihuahua
yaowei 23 24 NaN NaN baihuahua
tunwei 34 36 NaN NaN baihuahua
In [63]: frame2['datui'] = np.arange(3.)
In [64]: frame2
Out[64]:
jinxuanya piaoxiaomin qiamzjoxoam jinzhenxi datui
xiongwei 34 34 NaN NaN 0.0
yaowei 23 24 NaN NaN 1.0
tunwei 34 36 NaN NaN 2.0
当你将列表或数组赋值给一个列时,值的长度必须和DataFrame的长度相匹配。如果你将Series赋值给一列时,Series的索引将会按照DataFrame的索引重新排列,并在空缺的地方填充缺失值:
In [69]: val = pd.Series(['dahuahua1','baihuahua2'],index = ['xiongwei','tunwei'])
In [70]: frame2['datui'] = val
In [71]: frame2
Out[71]:
jinxuanya piaoxiaomin qiamzjoxoam jinzhenxi datui
xiongwei 34 34 NaN NaN dahuahua1
yaowei 23 24 NaN NaN NaN
tunwei 34 36 NaN NaN baihuahua2
如果被赋值的列并不存在,则会生成一个新的列。del关键字可以像字典中那样对DataFrame删除列.
在del的例子中,我首先增加一列,这一列是布尔值,判断条件时xiongwei列是否为‘34’:
In [74]: frame2['nicebody'] = frame2.jinxuanya == '34'
In [75]: frame2
Out[75]:
jinxuanya piaoxiaomin qiamzjoxoam jinzhenxi datui nicebody
xiongwei 34 34 NaN NaN dahuahua1 True
yaowei 23 24 NaN NaN NaN False
tunwei 34 36 NaN NaN baihuahua2 True
注意frame2['nicebody']可以创建新列,而frame2.nicebody不能创建新的列。
del方法可以用于移除之前新建的列:
In [77]: del frame2['nicebody']
In [78]: frame2
Out[78]:
jinxuanya piaoxiaomin qiamzjoxoam jinzhenxi datui
xiongwei 34 34 NaN NaN dahuahua1
yaowei 23 24 NaN NaN NaN
tunwei 34 36 NaN NaN baihuahua2
从DataFrame中选取的列是数据的视图,而不是拷贝。因此,对Series的修改会映射到DataFrame中。如果需要复制,则应当显式地使用Series的copy方法。
另一种常用的数据形式是包含字典的嵌套字典:
In [80]: data = {'piaoxiaomin':{'xiongwei':'34','yaowei':'24','tunwei':'36'},'jinxuanya':{'xiongwei':'34','yaowei':'23','tunwei':'
...: 34'}}
如果嵌套字典被赋值给DataFrame,pandas会将字典的键作为列,将内部字典的键作为行索引:
In [81]: frame3 = pd.DataFrame(data)
In [82]: frame3
Out[82]:
piaoxiaomin jinxuanya
xiongwei 34 34
yaowei 24 23
tunwei 36 34
你可以将使用类似numpy的语法对DataFrame进行转置操作(调换行和列):
In [83]: frame3.T
Out[83]:
xiongwei yaowei tunwei
piaoxiaomin 34 24 36
jinxuanya 34 23 34
内部字典的键被联合、排序后形成了结果的索引。如果已经显式指明索引的话,内部字典的键将不会被排序:
In [86]: pd.DataFrame(data,index=['xiongwei','datui','tunwei'])
Out[86]:
piaoxiaomin jinxuanya
xiongwei 34 34
datui NaN NaN
tunwei 36 34
包含Series的字典也可以用于构造DataFrame:
In [87]: pdata = {'jinxuanya':frame3['jinxuanya'][:-1],'piaoxiaomin':frame3['jinxuanya'][:-2]}
In [88]: pd.DataFrame(pdata)
Out[88]:
jinxuanya piaoxiaomin
xiongwei 34 34
yaowei 23 NaN
如果DataFrame的索引和列拥有name属性,则这些name属性也会被显示:
In [105]: frame3.index.name = 'sanwei';frame3.columns.name='name'
In [106]: frame3
Out[106]:
name piaoxiaomin jinxuanya
sanwei
xiongwei 34 34
yaowei 24 23
tunwei 36 34
和Series类似,DataFrame的values属性会将包含在DataFrame中的数据以二维ndarray的形式返回:
In [107]: frame3.values
Out[107]:
array([['34', '34'],
['24', '23'],
['36', '34']], dtype=object)
如果DataFrame的列是不同的dtypes,则values的dtype会自动选择适合所有列的类型:
In [108]: frame2.values
Out[108]:
array([['34', '34', nan, nan, 'dahuahua1'],
['23', '24', nan, nan, nan],
['34', '36', nan, nan, 'baihuahua2']], dtype=object)
5.1.3 索引对象
pandas中的索引对象是用于存储轴标签和其他元数据的(例如轴名称或标签)。在构造Series或DataFrame时,你所使用的任意数组或标签序列都可以在内部转换为索引对象:
In [109]: obj = pd.Series(range(3),index=['a','b','c'])
In [110]: index = obj.index
In [111]: index
Out[111]: Index(['a', 'b', 'c'], dtype='object')
索引对象是不可变的,因此用户是无法修改索引对象的:
In [113]: index[1] = 'd'
--------------------------------------------------------------------------
TypeError: Index does not support mutable operations
不变性使得在多种数据结构中分享索引对象更加安全:
In [114]: labels = pd.Index(np.arange(3))
In [115]: labels
Out[115]: Int64Index([0, 1, 2], dtype='int64')
In [116]: obj2 = pd.Series([1.5,-2.5,0],index=labels)
In [117]: obj2
Out[117]:
0 1.5
1 -2.5
2 0.0
dtype: float64
In [118]: obj2 is labels
Out[118]: False
一些用户并不经常利用索引对象提供的功能,但是因为一些操作会产生包含索引花数据的结果,理解索引如何工作还是很重要的。
除了类似数组,索引对象也像一个固定大小的集合:
In [119]: frame3
Out[119]:
name piaoxiaomin jinxuanya
sanwei
xiongwei 34 34
yaowei 24 23
tunwei 36 34
In [126]: frame3.columns
Out[126]: Index(['piaoxiaomin', 'jinxuanya'], dtype='object', name='name')
In [127]: 'piaoxiaomin'in frame3.columns
Out[127]: True
In [128]: 'jinzhenxi' in frame3.index
Out[128]: False
与Python集合不同,pandas索引对象可以包含重复标签:
In [129]: dup = pd.Index(['xiongwei','xiongwei','yaowei','tunwei'])
In [130]: dup
Out[130]: Index(['xiongwei', 'xiongwei', 'yaowei', 'tunwei'], dtype='object')
根据重复标签进行筛选,会选取所有重复标签对应的数据。
每个索引都有一些集合逻辑的方法和属性,这些方法和属性解决了关于它所包含的数据的其他常见问题。
5.2 基本功能
5.2.1 重建索引
reindex是pandas对象的重要方法,该方法用于创建一个符合新索引的新对象。考虑下面的例子:
In [132]: obj = pd.Series([4.5,7.2,-5.3,3.6],index=['a','b','c','d'])
In [133]: obj
Out[133]:
a 4.5
b 7.2
c -5.3
d 3.6
dtype: float64
Series调用reindex方法时,会将数据按照新的索引进行排列,如果某个索引值之前并不存在,则会引入缺失值:
In [134]: obj2 = obj.reindex(['a','b','c','d','e'])
In [135]: obj2
Out[135]:
a 4.5
b 7.2
c -5.3
d 3.6
e NaN
dtype: float64
对于顺序数据,比如时间序列,在重建索引时可能会需要进行插值或填值。method可选参数允许我们使用诸如ffill等方法在重建索引时插值,ffill方法会将值前向填充:
In [137]: obj3 = pd.Series(['blue','red','yellow'],index=[0,2,4,])
In [138]: obj3
Out[138]:
0 blue
2 red
4 yellow
dtype: object
In [139]: obj3.reindex(range(6),method='ffill')
Out[139]:
0 blue
1 blue
2 red
3 red
4 yellow
5 yellow
dtype: object
ffill:用前一个非缺失值去填充该缺失值
在DataFrame中,reindex可以改变行索引、列索引,也可以同时改变二者。当仅传入一个序列时,结果中的行会重建索引:
In [141]: frame = pd.DataFrame(np.arange(9).reshape((3,3)),index=['a','b','c'],columns=['quanxiaosheng','piaoxiaomin','jinzhenxi']
...: )
In [142]: frame
Out[142]:
quanxiaosheng piaoxiaomin jinzhenxi
a 0 1 2
b 3 4 5
c 6 7 8
In [143]: frame2 = frame.reindex(['a','b','c','d'])
In [144]: frame2
Out[144]:
quanxiaosheng piaoxiaomin jinzhenxi
a 0.0 1.0 2.0
b 3.0 4.0 5.0
c 6.0 7.0 8.0
d NaN NaN NaN
列可以使用columns关键字重建索引:
In [146]: states = ['jinxuanya','piaozhiyan','piaocaoe']
In [147]: frame.reindex(columns=states)
Out[147]:
jinxuanya piaozhiyan piaocaoe
a NaN NaN NaN
b NaN NaN NaN
c NaN NaN NaN
我们更深入地探索时,你可以使用loc进行更为简洁的标签索引,许多用户更倾向于是用这种方式:
In [162]: states= ['quanxiaosheng','piaoxiaomin','jinzhenxi']
In [163]: frame.loc[['a','b','c'],states]
Out[163]:
quanxiaosheng piaoxiaomin jinzhenxi
a 0 1 2
b 3 4 5
c 6 7 8
这里有个小意外,根据书上的案例,frame.loc[[‘a’,’b’,’c’,’e’],states],是带有’e’的。生成之后呢,e的那行,就都是NaN(因为没有数据嘛)。但是这么做之后,ipython发出报错:
KeyError: "Passing list-likes to .loc or [] with any missing labels is no longer supported. The following labels were missing: Index(['d'], dtype='object'). See https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#deprecate-loc-reindex-listlike"
意思似乎是不再支持“缺少至少一个标签”的情况。
根据官方文本的解读,建议替代方案是:
In [171]: frame.reindex(['a','b','c','d'],states)
<ipython-input-171-dd639d5b1969>:1: FutureWarning: Interpreting call
'.reindex(a, b)' as
'.reindex(index=a, columns=b)'.
Use named arguments to remove any ambiguity. In the future, using positional arguments for 'index' or 'columns' will raise a 'TypeError'.
frame.reindex(['a','b','c','d'],states)
Out[171]:
quanxiaosheng piaoxiaomin jinzhenxi
a 0.0 1.0 2.0
b 3.0 4.0 5.0
c 6.0 7.0 8.0
d NaN NaN NaN
5.2.2 轴向上删除条目
如果你已经拥有索引数组或不含条目的列表,在轴向上删除一个或更多的条目就非常容易,但这样需要一些数据操作和机核逻辑。drop方法会返回一个含有指示值或轴向上剔除值的新对象:
In [186]: obj = pd.Series(np.arange(5.),index=['a','b','c','d','e'])
In [187]: obj
Out[187]:
a 0.0
b 1.0
c 2.0
d 3.0
e 4.0
dtype: float64
In [188]: new_obj = obj.drop('c')
In [189]: new_obj
Out[189]:
a 0.0
b 1.0
d 3.0
e 4.0
dtype: float64
In [191]: obj.drop(['d','c'])
Out[191]:
a 0.0
b 1.0
e 4.0
dtype: float64
在DataFrame中,索引值可以轴向上删除。为了表明这个特性,我们首先创建一个示例:
In [192]: data= pd.DataFrame(np.arange(16).reshape((4,4)),index=['quanxiaosheng','piaoxiaomin','jinxuanya','jinzhenxi'],columns=['
...: one','two','three','four'])
In [193]: data
Out[193]:
one two three four
quanxiaosheng 0 1 2 3
piaoxiaomin 4 5 6 7
jinxuanya 8 9 10 11
jinzhenxi 12 13 14 15
在调用drop时使用标签序列会根据行标签删除值(0轴):
In [194]: data.drop(['jinxuanya','jinzhenxi'])
Out[194]:
one two three four
quanxiaosheng 0 1 2 3
piaoxiaomin 4 5 6 7
你可以通过传递axis = 1或axis='columns'来从列中删除值:
In [195]: data.drop(['two','four'],axis='columns')
Out[195]:
one three
quanxiaosheng 0 2
piaoxiaomin 4 6
jinxuanya 8 10
jinzhenxi 12 14
In [196]: data.drop(['two','four'],axis=1)
Out[196]:
one three
quanxiaosheng 0 2
piaoxiaomin 4 6
jinxuanya 8 10
jinzhenxi 12 14
很多函数,例如drop,会修改Series或Datafarame的尺寸或形状,这些方法直接操作原对象而不返回新对象:
In [197]: obj.drop('c',inplace=True)
In [198]: obj
Out[198]:
a 0.0
b 1.0
d 3.0
e 4.0
dtype: float64
请注意inplace属性,它会清除被删除的数据。
5.2.3 索引、选择与过滤
Series的索引(obj[...])与numpy数组索引的功能类似,只不过Series的索引值可以不仅仅是整数:
In [199]: obj = pd.Series(np.arange(4.),index=['a','b','c','d'])
In [200]: obj
Out[200]:
a 0.0
b 1.0
c 2.0
d 3.0
dtype: float64
In [201]: obj['b']
Out[201]: 1.0
In [202]: obj[1]
Out[202]: 1.0
In [203]: obj[2:4]
Out[203]:
c 2.0
d 3.0
dtype: float64
In [205]: obj[['a','b','c']]
Out[205]:
a 0.0
b 1.0
c 2.0
dtype: float64
In [206]: obj[[1,3]]
Out[206]:
b 1.0
d 3.0
dtype: float64
In [207]: obj[obj < 2]
Out[207]:
a 0.0
b 1.0
dtype: float64
普通的Python切片中是不包含尾部的,Series的切片与之不同:
In [212]: obj['b':'c'] # 从索引值b到c,且包含c
Out[212]:
b 1.0
c 2.0
dtype: float64
使用这些方法设值时会修改Series相应的部分:
In [213]: obj['b':'c'] = 5
In [214]: obj
Out[214]:
a 0.0
b 5.0
c 5.0
d 3.0
dtype: float64
使用单独值或序列,可以从DataFrame中索引处一个或多个列:
In [216]: data= pd.DataFrame(np.arange(16).reshape((4,4)),index=['quanxiaosheng','piaoxiaomin','jinxuanya','jinzhenxi'],columns=['
...: one','two','three','four'])
In [217]: data
Out[217]:
one two three four
quanxiaosheng 0 1 2 3
piaoxiaomin 4 5 6 7
jinxuanya 8 9 10 11
jinzhenxi 12 13 14 15
In [218]: data['two']
Out[218]:
quanxiaosheng 1
piaoxiaomin 5
jinxuanya 9
jinzhenxi 13
Name: two, dtype: int64
In [219]: data[['three','one']]
Out[219]:
three one
quanxiaosheng 2 0
piaoxiaomin 6 4
jinxuanya 10 8
jinzhenxi 14 12
这种索引方式也有特殊案例。首先,可以根据一个布尔值数组切片或选择数据:
In [221]: data[data['three']>5]
Out[221]:
one two three four
piaoxiaomin 4 5 6 7
jinxuanya 8 9 10 11
jinzhenxi 12 13 14 15
行选择语法data[:2]非常方便。传递单个元素或一个列表到[]符号中可以选择列。
另一个用例是使用布尔值DataFrame进行索引,布尔值DataFrame可以是对标量值进行比较产生的:
In [222]: data < 5
Out[222]:
one two three four
quanxiaosheng True True True True
piaoxiaomin True False False False
jinxuanya False False False False
jinzhenxi False False False False
In [223]: data[data<5] = 0
In [224]: data
Out[224]:
one two three four
quanxiaosheng 0 0 0 0
piaoxiaomin 0 5 6 7
jinxuanya 8 9 10 11
jinzhenxi 12 13 14 15
在这个特殊例子中,这种索引方式使得DataFrame在语法上更像Numpy的二维数组。
5.2.3.1 使用loc和iloc选择数据
针对DataFrame在行上的标签索引,我将介绍特殊的索引符号loc和iloc。他们允许你使用轴标签(loc)或整数标签(iloc)以numpy风格的语法从DataFrame中选出数组的行和列的子集。
In [225]: data.loc['quanxiaosheng',['one','four']]
Out[225]:
one 0
four 0
Name: quanxiaosheng, dtype: int64
然后我们使用整数标签iloc进行类似的数据选择:
In [226]: data.iloc[2,[3,0,1]]
Out[226]:
four 11
one 8
two 9
Name: jinxuanya, dtype: int64
In [227]: data.iloc[2]
Out[227]:
one 8
two 9
three 10
four 11
Name: jinxuanya, dtype: int64
In [228]: data.iloc[[1,2],[3,0,1]]
Out[228]:
four one two
piaoxiaomin 7 0 5
jinxuanya 11 8 9
除了单个标签或标签列表之外,索引功能还可以用于切片:
In [231]: data.loc[:'jinxuanya','two']
Out[231]:
quanxiaosheng 0
piaoxiaomin 5
jinxuanya 9
Name: two, dtype: int64
In [233]: data.iloc[:,:3][data.three > 5]
Out[233]:
one two three
piaoxiaomin 0 5 6
jinxuanya 8 9 10
jinzhenxi 12 13 14
因此,有多种方式可以选择、重排pandas对象中的数据。
这里我补充一下一张图,可能会帮助理解上述代码。
5.2.4 整数索引
在pandas对象使上用整数索引对新用户来说经常会产生歧义,这属于因为它和在列表、元组等Python内建数据结构上进行索引有些不同。例如,你可能认为下面的代码会产生错误:
In [240]: ser = pd.Series(np.arange(3.))
In [241]: ser
Out[241]:
0 0.0
1 1.0
2 2.0
dtype: float64
In [244]: ser[-1]
--------------------------------------------------------------------------
KeyError: -1
在上面的例子中,pandas可以“回退”到整数索引,但是这也的方式难免会引起一些微小的错误。假设我们有一个索引,它包含了0、1、2,但是推断用户所需要的索引方式是很难的。
另一方面,对于非整数索引,则不会有潜在的歧义:
In [242]: ser2 = pd.Series(np.arange(3.),index = ['a','b','c'])
In [243]: ser2[-1]
Out[243]: 2.0
为了保持一致性,如果你有一个包含整数的轴索引,数据选择时请始终使用标准索引。
为了更精确地处理,可以使用loc(用于标签)或iloc(用于整数):
In [245]: ser[:1]
Out[245]:
0 0.0
dtype: float64
In [246]: ser.loc[:1]
Out[246]:
0 0.0
1 1.0
dtype: float64
In [247]: ser.iloc[:1]
Out[247]:
0 0.0
dtype: float64
5.2.5 算术和数据对齐
不同索引的对象之间的算术行为是pandas提供给一些应用的一项重要特性。当你将对象相加时,如果存在某个索引对不相同,则返回结果的索引将是索引对的并集。对数据库用户来说,这个特性类似于索引标签的自动外链接。
In [251]: s1 = pd.Series([7.3,-2.5,3.4,1.5],index=['a','c','d','e'])
In [252]: s2 = pd.Series([-2.1,3.6,-1.5,4,3.1],index=['a','c','e','f','g'])
In [253]: s1
Out[253]:
a 7.3
c -2.5
d 3.4
e 1.5
dtype: float64
In [254]: s2
Out[254]:
a -2.1
c 3.6
e -1.5
f 4.0
g 3.1
dtype: float64
将这些对象相加会产生:
In [255]: s1 + s2
Out[255]:
a 5.2
c 1.1
d NaN
e 0.0
f NaN
g NaN
dtype: float64
没有交接的标签位置上,内部数据对齐会产生缺失值。缺失值会在后续的算术操作上产生影响。在DataFrame的示例中,行和列上都会执行对齐:
In [256]: df1 = pd.DataFrame(np.arange(9.).reshape((3,3)),columns=list('bcd'),index=['quanxiaosheng','piaoxiaomin','jinzhenxi'])
In [257]: df2 = pd.DataFrame(np.arange(12.).reshape((4,3)),columns=list('bde'),index=['piaozhiyan','piaoxiaomin','jinzhenxi','piao
...: caoe'])
In [258]: df1
Out[258]:
b c d
quanxiaosheng 0.0 1.0 2.0
piaoxiaomin 3.0 4.0 5.0
jinzhenxi 6.0 7.0 8.0
In [259]: df2
Out[259]:
b d e
piaozhiyan 0.0 1.0 2.0
piaoxiaomin 3.0 4.0 5.0
jinzhenxi 6.0 7.0 8.0
piaocaoe 9.0 10.0 11.0
将这些对象加在一起,返回一个DataFrame,它的索引、列是每个DataFrame的索引、列的并集。
In [260]: df1+df2
Out[260]:
b c d e
jinzhenxi 12.0 NaN 15.0 NaN
piaocaoe NaN NaN NaN NaN
piaoxiaomin 6.0 NaN 9.0 NaN
piaozhiyan NaN NaN NaN NaN
quanxiaosheng NaN NaN NaN NaN
由于'c'列和'e'列并不是两个dataframe共有的列,这两列中产生了缺失值。对于行标签不同的dataframe对象也是如此。
如果你将两个行或列完全不同的dataframe对象相加,结果将全部为空:
In [261]: df1 = pd.DataFrame({'A':[1,2]})
In [262]: df2 = pd.DataFrame({'B':[3,4]})
In [263]: df1
Out[263]:
A
0 1
1 2
In [264]: df2
Out[264]:
B
0 3
1 4
In [265]: df1 -df2
Out[265]:
A B
0 NaN NaN
1 NaN NaN
5.2.5.1 使用填充值的算术方法
在两个不同的索引化对象之间进行算术操作时,你可能会想要使用特殊填充值,比如当轴标签在一个对象中存在,另一个对象中不存在时,你想将缺失值填充为0:
In [268]: df1 = pd.DataFrame(np.arange(12.).reshape((3,4)),columns=list('abcd'))
In [269]: df2 = pd.DataFrame(np.arange(20.).reshape((4,5)),columns=list('abcde'))
In [270]: df2.loc[1,'b'] = np.nan
In [271]: df1
Out[271]:
a b c d
0 0.0 1.0 2.0 3.0
1 4.0 5.0 6.0 7.0
2 8.0 9.0 10.0 11.0
In [272]: df2
Out[272]:
a b c d e
0 0.0 1.0 2.0 3.0 4.0
1 5.0 NaN 7.0 8.0 9.0
2 10.0 11.0 12.0 13.0 14.0
3 15.0 16.0 17.0 18.0 19.0
将这些df添加到一起会导致一些不重叠的位置出现NA值:
In [273]: df1+df2
Out[273]:
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作为参数传入:
In [274]: df1.add(df2,fill_value=0)
Out[274]:
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
书说至此,书上就没有关于上面这段代码的介绍了。那么add方法和fill_value参数到底干啥用的呢?
add方法:将对象作为一个整体加入到字典中。
fill_value参数:如果df1内的value=NaN,那么value等于fill_value,然后与df1中相同索引的value相加。所以上述代码的意思就是,首先先计算df2进入df1里会出现多少缺失值,如果碰到缺失值则赋值:df2的相同索引的值+0。
下图是Series和DataFrame的算术方法。这些方法中的每一个都有一个以r开头的副本,这些副本方法的参数是翻转的。因此下面两个语句的结果是等价的:
In [283]: 1 / df1
Out[283]:
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
In [284]: df1.rdiv(1)
Out[284]:
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
与此相关的一点,当对Series或DataFrame重建索引时,你也可以指定一个不同的填充值:
In [285]: df1.reindex(columns=df2.columns,fill_value=0)
Out[285]:
a b c d e
0 0.0 1.0 2.0 3.0 0
1 4.0 5.0 6.0 7.0 0
2 8.0 9.0 10.0 11.0 0
5.2.5.2 DataFrame和Series间的操作
DataFrame和Series间的算术操作与Numpy中不同维度数组见的操作类似。首先,在下面的生动示例中,考虑二维数组和其中一行之间的区别:
In [292]: arr = np.arange(12.).reshape((3,4))
In [293]: arr
Out[293]:
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
In [294]: arr[0]
Out[294]: array([0., 1., 2., 3.])
In [295]: arr - arr[0]
Out[295]:
array([[0., 0., 0., 0.],
[4., 4., 4., 4.],
[8., 8., 8., 8.]])
补充一下,这里是arr的每一行都减[0,1,2,3]
当我们从arr中减去arr[0]时,每一行都进行了操作。这就是所谓的广播机制。DataFrame和Series间的操作是类似的:
In [298]: frame = pd.DataFrame(np.arange(12.).reshape((4,3)),columns=list('bde'),index=['quanxiaosheng','piaoxiaomin','piaozhiyan'
...: ,'jinxuanya'])
In [299]: series = frame.iloc[0]
In [300]: frame
Out[300]:
b d e
quanxiaosheng 0.0 1.0 2.0
piaoxiaomin 3.0 4.0 5.0
piaozhiyan 6.0 7.0 8.0
jinxuanya 9.0 10.0 11.0
In [301]: series
Out[301]:
b 0.0
d 1.0
e 2.0
Name: quanxiaosheng, dtype: float64
默认情况下,dataframe和series的数学操作中会将series的索引和dataframe的列进行匹配,并广播到各行:
In [302]: frame - series
Out[302]:
b d e
quanxiaosheng 0.0 0.0 0.0
piaoxiaomin 3.0 3.0 3.0
piaozhiyan 6.0 6.0 6.0
jinxuanya 9.0 9.0 9.0
如果一个索引值不在dataframe的列里,也不在series的索引中,则对象会重建索引并形成联合:
In [303]: series2 = pd.Series(range(3),index=['b','e','f'])
In [304]: frame + series2
Out[304]:
b d e f
quanxiaosheng 0.0 NaN 3.0 NaN
piaoxiaomin 3.0 NaN 6.0 NaN
piaozhiyan 6.0 NaN 9.0 NaN
jinxuanya 9.0 NaN 12.0 NaN
如果你想改为在列上进行广播,在行上匹配,你必须使用算术方法中的一种。
In [305]: series3 = frame['d']
In [306]: frame
Out[306]:
b d e
quanxiaosheng 0.0 1.0 2.0
piaoxiaomin 3.0 4.0 5.0
piaozhiyan 6.0 7.0 8.0
jinxuanya 9.0 10.0 11.0
In [307]: series3
Out[307]:
quanxiaosheng 1.0
piaoxiaomin 4.0
piaozhiyan 7.0
jinxuanya 10.0
Name: d, dtype: float64
In [308]: frame.sub(series3,axis='index')
Out[308]:
b d e
quanxiaosheng -1.0 0.0 1.0
piaoxiaomin -1.0 0.0 1.0
piaozhiyan -1.0 0.0 1.0
jinxuanya -1.0 0.0 1.0
你传递的axis值是匹配轴的。上面的示例中表示我们需要在dataframe的行索引上对行匹配(axis='index' 或 axis=0),并进行广播。
这里值得说一下,简单来说,就是把frame和series3对应的列,进行相减(frame – series3)。
5.2.6 函数应用和映射
Numpy的通用函数(逐元素数组方法)对pandas对象也有效:
In [310]: frame = pd.DataFrame(np.random.randn(4,3),columns=list('bde'),index=['quanxiaosheng','piaoxiaomin','piaozhiyan' ,'jinxua
...: nya'])
In [311]: frame
Out[311]:
b d e
quanxiaosheng -1.776766 -1.222338 -0.338254
piaoxiaomin -0.010509 -0.714661 1.071737
piaozhiyan 1.520708 0.440294 -1.095024
jinxuanya 0.918618 -1.093099 -1.342356
In [312]: np.abs(frame) # 取绝对值
Out[312]:
b d e
quanxiaosheng 1.776766 1.222338 0.338254
piaoxiaomin 0.010509 0.714661 1.071737
piaozhiyan 1.520708 0.440294 1.095024
jinxuanya 0.918618 1.093099 1.342356
另一个常用的操作是将函数应用到一行或一列的一位数组上。dataframe的apply方法可以实现这个功能:
In [313]: f = lambda x :x.max() - x.min()
In [314]: frame.apply(f)
Out[314]:
b 3.297474
d 1.662632
e 2.414093
dtype: float64
这里的函数f,可以计算series最大值和最小值的差,会被frame中的每一列调用一次。结果是一个以frame的列作为索引的series。
如果你传递axis = 'columns'给apply函数,函数将会被每行调用一次:
In [318]: frame.apply(f,axis='columns')
Out[318]:
quanxiaosheng 1.438512
piaoxiaomin 1.786398
piaozhiyan 2.615732
jinxuanya 2.260974
dtype: float64
大部分最常用的数组统计(比如sum和mean)都是dataframe的方法,因此计算统计值时使用apply并不是必须的。
传递给apply的函数并不一定要返回一个标量值,也可以返回带有多个值的series:
In [319]: def f(x):
...: return pd.Series([x.min(),x.max()],index=['min','max'])
...:
In [320]: frame.apply(f)
Out[320]:
b d e
min -1.776766 -1.222338 -1.342356
max 1.520708 0.440294 1.071737
逐元素的Python函数也可以使用。假设你想要根据frame中的每个浮点数计算一个格式化字符串,可以使用applymap方法:
In [321]: format = lambda x: '%.2f' % x
In [322]: frame.applymap(format)
Out[322]:
b d e
quanxiaosheng -1.78 -1.22 -0.34
piaoxiaomin -0.01 -0.71 1.07
piaozhiyan 1.52 0.44 -1.10
jinxuanya 0.92 -1.09 -1.34
使用applymap作为函数名是因为series有map方法,可以将一个逐元素的函数应用在series上:
In [323]: frame['e'].map(format)
Out[323]:
quanxiaosheng -0.34
piaoxiaomin 1.07
piaozhiyan -1.10
jinxuanya -1.34
Name: e, dtype: object
5.2.7 排序和排名
根据某些准则对数据集进行排序是另一个重要的内建操作。如需按行或列索引进行字典型排序,需要使用sort_index方法,该方法返回一个新的、排序好的对象:
In [325]: obj = pd.Series(range(4),index=['d','a','b','c'])
In [326]: obj.sort_index()
Out[326]:
a 1
b 2
c 3
d 0
dtype: int64
在dataframe中,你可以在各个轴上按索引排序:
In [327]: frame = pd.DataFrame(np.arange(8).reshape((2,4)),index=['three','one'],columns=['d','a','b','c'])
In [328]: frame.sort_index()
Out[328]:
d a b c
one 4 5 6 7
three 0 1 2 3
In [329]: frame.sort_index(axis=1)
Out[329]:
a b c d
three 1 2 3 0
one 5 6 7 4
数据默认会升序排序,但是也可以按照降序排序:
In [332]: frame.sort_index(axis=1,ascending=False)
Out[332]:
d c b a
three 0 3 2 1
one 4 7 6 5
如果要根据series的值进行排序,使用sort_values方法:
In [333]: obj = pd.Series([4,7,-3,2])
In [334]: obj
Out[334]:
0 4
1 7
2 -3
3 2
dtype: int64
In [335]: obj.sort_values()
Out[335]:
2 -3
3 2
0 4
1 7
dtype: int64
默认情况下,所有的缺失值都会被排序到series的尾部:
In [336]: obj = pd.Series([4,np.nan,7,np.nan,-3,2])
In [337]: obj.sort_values()
Out[337]:
4 -3.0
5 2.0
0 4.0
2 7.0
1 NaN
3 NaN
dtype: float64
当对DataFrame排序时,你可以使用一列或多列作为排序键。为了实现这个功能,传递一个或多个列名给sort_values的可选参数by:
In [338]: frame = pd.DataFrame({'b':[4,7,-3,2],'a':[0,1,0,1]})
In [339]: frame
Out[339]:
b a
0 4 0
1 7 1
2 -3 0
3 2 1
In [340]: frame.sort_values(by='b')
Out[340]:
b a
2 -3 0
3 2 1
0 4 0
1 7 1
对多列排序时,传递列名的列表:
In [341]: frame.sort_values(by=['a','b'])
Out[341]:
b a
2 -3 0
0 4 0
3 2 1
1 7 1
排名是指对数组从1到有效数据点总数分配名次的操作。series和datafeame的rank方法是实现排名的方法,默认情况下,rank通过将平均排名分配到每个组来打破平级关系:
In [342]: obj = pd.Series([7,-5,7,4,2,0,4])
In [343]: obj.rank()
Out[343]:
0 6.5
1 1.0
2 6.5
3 4.5
4 3.0
5 2.0
6 4.5
dtype: float64
这里需要补充两个点:
1、首先我们要明确,这里的位置是指该数字在原来组合里的位置,比如组合里的-5,在rank后,显示位置是1.0。
2、6.5和4.5是怎么来的:[7,-5,7,4,2,0,4]这个序列,按从小到大排序应该是[-5,0,2,4,4,7,7]。但是有两个4和两个7。那怎么排序呢?很简单,按照排序好的逻辑,第6个位置和第七个位置都是7,那么就(6+7)/2=6.5,7这个数字排在6.5上。4.5也是同理。
排名也可以根据他们在数据中的观察顺序进行分配:
In [344]: obj.rank(method='first')
Out[344]:
0 6.0
1 1.0
2 7.0
3 4.0
4 3.0
5 2.0
6 5.0
在上面的例子中,对条目0和2设置的名次为6和7,而不是之前的平均排名6.5,是因为在数据标签中标签0在标签2前面。
你可以按降序排名:
In [345]: obj.rank(ascending=False,method='max')
Out[345]:
0 2.0
1 7.0
2 2.0
3 4.0
4 5.0
5 6.0
6 4.0
dtype: float64
下图是可用的平级关系打破方法列表:
DataFrame 可以对行或列计算排名:
In [346]: frame = pd.DataFrame({'b':[4.3,7,-3,2],'a':[0,1,0,1],'c':[-2,5,8,-2.5]})
In [347]: frame
Out[347]:
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
In [348]: frame.rank(axis='columns')
Out[348]:
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
5.2.8 含有重复标签的轴索引
目前为止我们所见过的示例中,轴索引都是唯一的(索引值)。尽管很多pandas函数(比如reindex)需要标签是唯一的,但这个并不是强制性的。让我们考虑一个小型的带有重复索引的series:
In [349]: obj = pd.Series(range(5),index=['a','a','b','b','c'])
In [350]: obj
Out[350]:
a 0
a 1
b 2
b 3
c 4
dtype: int64
索引的is_unique属性可以告诉你它的标签是否唯一:
In [351]: obj.index.is_unique
Out[351]: False
带有重复索引的情况下,数据选择是与之前操作有差别的主要情况。根据一个标签索引多个条目会返回一个序列,而单个条目会返回标量值:
In [352]: obj['a']
Out[352]:
a 0
a 1
dtype: int64
In [353]: obj['c']
Out[353]: 4
这可能会使代码更复杂,因为来自索引的输出类型可能因标签是否重复而有所不同。
相同的逻辑可以拓展到在dataframe中进行索引:
In [359]: df2 = pd.DataFrame(np.arange(18.).reshape((6,3)),columns=list('bde'),index=['piaozhiyan','piaoxiaomin','jinzhenxi','piao
...: caoe','jinzhenxi','piaoxiaomin'])
In [360]: df2
Out[360]:
b d e
piaozhiyan 0.0 1.0 2.0
piaoxiaomin 3.0 4.0 5.0
jinzhenxi 6.0 7.0 8.0
piaocaoe 9.0 10.0 11.0
jinzhenxi 12.0 13.0 14.0
piaoxiaomin 15.0 16.0 17.0
In [361]: df2.loc['piaoxiaomin']
Out[361]:
b d e
piaoxiaomin 3.0 4.0 5.0
piaoxiaomin 15.0 16.0 17.0
5.3 描述性统计的概述与计算
pandas对象装配了一个常用数学、统计学方法的集合。其中大部分属于归约或汇总统计的类别,这些方法从dataframe的行或列中抽取一个series或一系列值的单个值(如总和或平均值)。与numpy数组中的类似方法相比,它们内建了处理缺失值的功能,考虑一个小型的dataframe。
In [363]: 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'])
In [364]: df
Out[364]:
one two
a 1.40 NaN
b 7.10 -4.5
c NaN NaN
d 0.75 -1.3
调用dataframe的sum方法返回一个包含列上加和的series:
In [364]: df
Out[364]:
one two
a 1.40 NaN
b 7.10 -4.5
c NaN NaN
d 0.75 -1.3
In [365]: df.sum()
Out[365]:
one 9.25
two -5.80
dtype: float64
传入axis='columns'或axis=1,则会将一行上各个列的值相加:
In [366]: df.sum(axis='columns')
Out[366]:
a 1.40
b 2.60
c 0.00
d -0.55
dtype: float64
除非整个切片(本例上就是行或列)上都是NA,否则NA值是被自动排除的。可以通过禁用skipna来实现不排除NA值:
In [367]: df.mean(axis='columns',skipna=False)
Out[367]:
a NaN
b 1.300
c NaN
d -0.275
dtype: float64
下图是归约方法的常用可选参数列表:
一些方法,比如idxmin和idxmax,返回的是间接统计信息,比如最小值或最大值的索引值:
In [368]: df.idxmax()
Out[368]:
one b
two d
dtype: object
除了归约方法外,有的方法是积累型方法:
In [369]: df.cumsum()
Out[369]:
one two
a 1.40 NaN
b 8.50 -4.5
c NaN NaN
d 9.25 -5.8
还有一类方法既不是归约型方法也不是积累型方法。describe就是其中之一,它一次性产生多个汇总统计:
In [371]: df.describe()
Out[371]:
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产生另一种汇总统计:
In [372]: obj = pd.Series(['quanxiaosheng','quanxiaosheng','jinzhenxi','piaoxiaomin']*4)
In [373]: obj.describe()
Out[373]:
count 16 # 一共16个字符串
unique 3 # 排重后是3个字符串
top quanxiaosheng # 出现最多的是‘quanxiaosheng’
freq 8 # 一个字符串最多出现了八次
dtype: object
下图是汇总统计及其相关方法的完整列表:
5.3.1 相关性和协方差
一些汇总统计,比如相关性和协方差,是由多个参数计算出的。考虑某些使用附加pandas-datareader库从雅虎上获取的包括股价和交易量的dataframe。如果你还没有安装它,可以通过conda或者pip进行安装:
conda install pandas-datareader
import pandas_datareader.data as web
import pandas as pd
all_data={ticker:web.get_data_yahoo(ticker) for ticker in ['AAPL','IBM','MSFT','GOOG']}
price = pd.DataFrame({ticker:data['Ad Close'] for ticker,data in all_data.items()})
volume = pd.DataFrame({ticker:data['Volume'] for ticker,data in all_data.items()})
现在我计算股价的百分比:
In [7]: returns = price.pct_change()
In [8]: returns.tail()
Out[8]:
AAPL IBM MSFT GOOG
Date
2021-03-19 -0.004480 -0.008919 -0.001604 0.003428
2021-03-22 0.028336 0.012801 0.024484 -0.002256
2021-03-23 -0.006889 -0.000689 0.006738 0.007049
2021-03-24 -0.019994 0.001226 -0.008923 -0.003848
2021-03-25 0.005080 -0.005742 0.003313 0.002411
Series的corr方法计算的是两个series中重叠的,非NA的,按索引对齐的值的相关性。相应地,cov计算的是协方差:
In [9]: returns.corr()
Out[9]:
AAPL IBM MSFT GOOG
AAPL 1.000000 0.446896 0.712947 0.651114
IBM 0.446896 1.000000 0.532985 0.500124
MSFT 0.712947 0.532985 1.000000 0.778763
GOOG 0.651114 0.500124 0.778763 1.000000
In [10]: returns.cov()
Out[10]:
AAPL IBM MSFT GOOG
AAPL 0.000365 0.000140 0.000236 0.000209
IBM 0.000140 0.000268 0.000151 0.000137
MSFT 0.000236 0.000151 0.000301 0.000227
GOOG 0.000209 0.000137 0.000227 0.000281
协方差:
可以通俗的理解为:两个变量在变化过程中是同方向变化?还是反方向变化?同向或反向程度如何?
你变大,同时我也变大,说明两个变量是同向变化的,这时协方差就是正的。
你变大,同时我变小,说明两个变量是反向变化的,这时协方差就是负的。
从数值来看,协方差的数值越大,两个变量同向程度也就越大。反之亦然。
转自知乎答主:GRAYLAMBGRAYLAMB
使用DataFrame的corrwith方法,你可以计算出dataFrame中的行或列与领一个序列或DataFrame的相关性。该方法传入一个series时,会返回一个含有为每列计算相关性值的seies:
n [11]: returns.corrwith(returns.IBM)
Out[11]:
AAPL 0.446896
IBM 1.000000
MSFT 0.532985
GOOG 0.500124
dtype: float64
传入一个DataFrame时,会计算匹配到列名的相关性数值。在这里,我计算出交易量百分比变化的相关性:
In [13]: returns.corrwith(volume)
Out[13]:
AAPL -0.074007
IBM -0.114693
MSFT -0.084778
GOOG -0.134113
dtype: float64
传入axis=‘columns’会逐行地进行计算。在所有例子中,在计算相关性之前,数据点已经按标签进行了对齐。
5.3.2 唯一值、计数和成员属性
另一类相关的方法可以从一维series包含的数值中提取信息。为了说明这些方法,请考虑这个例子:
obj = pd.Series(['c','a','d','a','a','b','b','c','c'])
第一个函数是unique = obj.unique
In [14]: obj = pd.Series(['c','a','d','a','a','b','b','c','c'])
In [15]: unique = obj.unique()
In [16]: unique
Out[16]: array(['c', 'a', 'd', 'b'], dtype=object)
唯一值并不一定按照排序好的顺序返回,但是如果需要的话可以进行排序(uniques.sort())。相应的,value_counts计算Series包含的值的个数:
In [17]: obj.value_counts()
Out[17]:
c 3
a 3
b 2
d 1
dtype: int64
为了方便,返回的Series会按照数量降序排序。value_counts也是有效的pandas顶层方法,可以用于任意数组或序列:
In [18]: pd.value_counts(obj.values,sort=False)
Out[18]:
a 3
d 1
c 3
b 2
dtype: int64
isin执行向量化的成员属性检查,还可以将数据集以Series或DataFrame一列的形式过滤为数据集的值子集:
In [19]: obj
Out[19]:
0 c
1 a
2 d
3 a
4 a
5 b
6 b
7 c
8 c
dtype: object
In [20]: mask = obj.isin(['b','c'])
In [21]: mask
Out[21]:
0 True
1 False
2 False
3 False
4 False
5 True
6 True
7 True
8 True
dtype: bool
In [22]: obj[mask]
Out[22]:
0 c
5 b
6 b
7 c
8 c
dtype: object
与isin相关的Index.get_indexer方法,可以提供一个索引数组,这个索引数组可以将可能非唯一值数组转换为另一个唯一值数组:
In [23]: match = pd.Series(['c','a','b','b','c','a'])
In [24]: vals = pd.Series(['c','b','a'])
In [26]: pd.Index(vals).get_indexer(match) # vals在match中的位置
Out[26]: array([0, 2, 1, 1, 0, 2])
某些情况下,你可能想要计算dataframe多个相关性的直方图,如下面的例子:
In [28]: data = pd.DataFrame({'qu1':[1,3,4,3,4,],'qu2':[2,3,1,2,3,],'qu3':[1,5,2,4,4]})
In [29]: data
Out[29]:
qu1 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函数可以得到:
In [30]: result = data.apply(pd.value_counts).fillna(0)
In [31]: result
Out[31]:
qu1 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
这里,结果中的行标签是所有列中出现的不同值,数值则是这些不同值在每个列中出现的次数。
比如qu1,1在qu1列中只出现了一次,所以数字为1。2在qu1列中出现0次,所以为0。3在qu1列中出现了两次,所以为2。以此类推。
第五章完结,我们明天再见~
胭惜雨
2021年03月25日