北理工Python数据分析及展示之Pandas。北理工嵩天老师的Python系列课数据分析与展示最后一章的内容。
加油!
Numpy的扩展,但是关注的重点与Numpy不同,Numpy注重的是数据的组织结构,提供更为便捷的数组运算。而Pandas的目标是进行数据分析,更注重数据与索引的关系。
默认方法:
import pandas as pd
验证安装pandas是否成功:
In [1]: import pandas as pd
In [2]: d = pd.Series(range(20))
In [3]: d
Out[3]:
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
11 11
12 12
13 13
14 14
15 15
16 16
17 17
18 18
19 19
dtype: int64
In [4]: d.cumsum()
Out[4]:
0 0
1 1
2 3
3 6
4 10‵‵‵
5 15
6 21
7 28
8 36
9 45
10 55
11 66
12 78
13 91
14 105
15 120
16 136
17 153
18 171
19 190
dtype: int64
说明安装成功。
由一维ndarray以及对应的索引组成。也就是说一个Series对象包含一个一维ndarray数组和一个index对象。
比如:
In [4]: a = pd.Series([9, 8, 7, 6])
In [5]: a
Out[5]:
0 9
1 8
2 7
3 6
dtype: int64
左侧一列数据表示自动索引,0,1,2,3
右侧一列数据表示数组9,8,7,6
元素类型为np.int64
也可以自己定义索引:
In [6]: b = pd.Series([9, 8, 7, 6], index=['a', 'b', 'c', 'd'])
In [7]: b
Out[7]:
a 9
b 8
c 7
d 6
dtype: int64
可以看到索引变成了a,b,c,d
可以从以下类型创建:
列表
标量
In [9]: s = pd.Series(25, index=['a', 'b', 'c'])
In [10]: s
Out[10]:
a 25
b 25
c 25
dtype: int64
可以看到有3个值为25,索引不同的数据,在这里不能省略index=
字典
In [11]: d = pd.Series({'a':9, 'b':8, 'c':7})
In [12]: d
Out[12]:
a 9
b 8
c 7
dtype: int64
可以看到创建了顺序对应的Series。
还可以这样:
In [13]: e = pd.Series({'a':9, 'b':8, 'c':7}, index=['c', 'a', 'b', 'd'])
In [14]: e
Out[14]:
c 7.0
a 9.0
b 8.0
d NaN
dtype: float64
可以看到,自己指定索引时可以与之前的顺序不同,也可以增加索引。可以看作后面index参数是在选择字典中的数据。
ndarray
In [15]: n = pd.Series(np.arange(5))
In [16]: n
Out[16]:
0 0
1 1
2 2
3 3
4 4
dtype: int64
In [17]: m = pd.Series(np.arange(5), index=np.arange(9, 4, -1))
In [18]: m
Out[18]:
9 0
8 1
7 2
6 3
5 4
dtype: int64
其他函数
如range等。
取数据:
In [22]: b.index
Out[22]: Index(['a', 'b', 'c', 'd'], dtype='object')
In [23]: b.values
Out[23]: array([9, 8, 7, 6])
index和values,index是一种类型专门保存索引。而values是ndarray类型。
索引:
In [24]: b[0]
Out[24]: 9
In [25]: b['a']
Out[25]: 9
In [30]: b[['a', 'b', 'c']]
Out[30]:
a 9
b 8
c 7
dtype: int64
混合索引貌似在当前版本已经不支持了。
切片索引:
In [33]: b = pd.Series([9, 8, 7, 6], ['a', 'b', 'c', 'd'])
In [34]: b
Out[34]:
a 9
b 8
c 7
d 6
dtype: int64
In [35]: b[3]
Out[35]: 6
In [36]: b[:3]
Out[36]:
a 9
b 8
c 7
dtype: int64
In [37]: type(b[3])
Out[37]: numpy.int64
In [38]: type(b[:3])
Out[38]: pandas.core.series.Series
**注意:**切片索引出的数据还是Series类型。
Series类型也保留了numpy中的一些操作:
In [39]: b[b > b.median()]
Out[39]:
a 9
b 8
dtype: int64
In [40]: np.exp(b)
Out[40]:
a 8103.083928
b 2980.957987
c 1096.633158
d 403.428793
dtype: float64
类似于字典的操作:
In [41]: b['b']
Out[41]: 8
In [42]: 'c' in b
Out[42]: True
In [43]: 0 in b
Out[43]: False
In [44]: b.get('f', 100)
Out[44]: 100
get
成员函数如果有’f’则返回对应的值,没有返回100.
Series对齐问题:
In [45]: a = pd.Series([1, 2, 3], ['c', 'd', 'e'])
In [46]: b = pd.Series([9, 8, 7, 6], ['a', 'b', 'c', 'd'])
In [47]: a + b
Out[47]:
a NaN
b NaN
c 8.0
d 8.0
e NaN
dtype: float64
没有对应的值,结果为NaN,都有对应的值,则对应相加,也就是根据索引进行的运算。
Series的name属性以及索引的name属性:
In [48]: b.name
In [49]: b.name = 'Series对象'
In [50]: b.index.name = '索引列'
In [51]: b
Out[51]:
索引列
a 9
b 8
c 7
d 6
Name: Series对象, dtype: int64
Series赋值:
In [56]: b['b']
Out[56]: 8
In [57]: b['a'] = 15
In [58]: b['b', 'c'] = 20
%也可以b[['b', 'c']] = 20
In [59]: b
Out[59]:
索引列
a 15
b 20
c 20
d 6
Name: Series对象, dtype: int64
Series是一维数据构成,那DataFrame就是二维类型。
index代表行数据,axis=0;coloum代表列数据,axis=1。
可以由:
二维ndarray对象
In [104]: d = pd.DataFrame(np.arange(10).reshape(2, 5))
In [105]: d
Out[105]:
0 1 2 3 4
0 0 1 2 3 4
1 5 6 7 8 9
最左边的列和最上面的行都是自动索引。
一维ndarray,列表,字典,元组或者Series构成的字典
In [111]: dt = {'one': pd.Series([1, 2, 3], index = ['a', 'b', 'c']),
...: 'two': pd.Series([9, 8, 7, 6], index = ['a', 'b', 'c', 'd'])}
In [112]: d = pd.DataFrame(dt)
%行索引就是字典中Series中的index,列索引就是字典的键,没有值自动补齐,填充NaN
In [113]: d
Out[113]:
one two
a 1.0 9
b 2.0 8
c 3.0 7
d NaN 6
%自定义列索引,three没有,自己不全NaN
In [114]: pd.DataFrame(dt, index=['b', 'c', 'd'], columns=['two', 'three'])
Out[114]:
two three
b 8 NaN
c 7 NaN
d 6 NaN
DataFrame关心数据,索引如果给定就用给定的,而行索引来自index,列索引来自字典的键。
import numpy as np
import pandas as pd
dl = {'城市': ['北京', '上海', '广州', '深圳', '沈阳'],
'环比': [101.5, 101.2, 101.3, 102.0, 100.1],
'同比': [120.7, 127.3, 119.4, 140.9, 101.4],
'定基': [121.4, 127.8, 120.0, 145.5, 101.6]}
d = pd.DataFrame(dl, index=['c1', 'c2', 'c3', 'c4', 'c5'])
print(d)
In [2]: d
Out[2]:
城市 环比 同比 定基
c1 北京 101.5 120.7 121.4
c2 上海 101.2 127.3 127.8
c3 广州 101.3 119.4 120.0
c4 深圳 102.0 140.9 145.5
c5 沈阳 100.1 101.4 101.6
In [3]: d.index
Out[3]: Index(['c1', 'c2', 'c3', 'c4', 'c5'], dtype='object')
In [4]: d.columns
Out[4]: Index(['城市', '环比', '同比', '定基'], dtype='object')
In [5]: d.values
Out[5]:
array([['北京', 101.5, 120.7, 121.4],
['上海', 101.2, 127.3, 127.8],
['广州', 101.3, 119.4, 120.0],
['深圳', 102.0, 140.9, 145.5],
['沈阳', 100.1, 101.4, 101.6]], dtype=object)
这里虽然看着生成的DataFrame和之前的顺序相同,实则是无序的,因为字典是无序的。
这里课程录制的时间有点早应该是2017年,课程录制完成后DataFrame的ix方法被弃用,改为更为精确的loc和iloc。
loc方法用于根据行索引名字来索引,iloc根据行号索引:
In [24]: d['同比']
Out[24]:
c1 120.7
c2 127.3
c3 119.4
c4 140.9
c5 101.4
Name: 同比, dtype: float64
In [25]: d.loc['c2']
Out[25]:
城市 上海
环比 101.2
同比 127.3
定基 127.8
Name: c2, dtype: object
In [26]: d.iloc[1]
Out[26]:
城市 上海
环比 101.2
同比 127.3
定基 127.8
Name: c2, dtype: object
In [27]: d['同比']['c2']
Out[27]: 127.3
Series类型
其他的DataFrame类型
reindex
函数:
In [28]: d
Out[28]:
城市 环比 同比 定基
c1 北京 101.5 120.7 121.4
c2 上海 101.2 127.3 127.8
c3 广州 101.3 119.4 120.0
c4 深圳 102.0 140.9 145.5
c5 沈阳 100.1 101.4 101.6
In [29]: d = d.reindex(index=['c5', 'c4', 'c3', 'c2', 'c1'])
In [30]: d
Out[30]:
城市 环比 同比 定基
c5 沈阳 100.1 101.4 101.6
c4 深圳 102.0 140.9 145.5
c3 广州 101.3 119.4 120.0
c2 上海 101.2 127.3 127.8
c1 北京 101.5 120.7 121.4
reindex
参数解释(不全,全部自己看帮助文档)
args | comment |
---|---|
index, columns | 新的行列自定义索引 |
fill_value | 重新索引中,用于填充确实位置的值 |
method | 填充方法,ffill当前值向前填充,bfill向后填充 |
limit | 最大填充量 |
copy | 默认True,生成新对象,否则,如果新旧相等,不生成新对象 |
fill_value
参数实例:
In [3]: newc = d.columns.insert(4, '新增')
In [4]: newd = d.reindex(columns=newc, fill_value=200)
In [5]: newd
Out[5]:
城市 环比 同比 定基 新增
c1 北京 101.5 120.7 121.4 200
c2 上海 101.2 127.3 127.8 200
c3 广州 101.3 119.4 120.0 200
c4 深圳 102.0 140.9 145.5 200
c5 沈阳 100.1 101.4 101.6 200
Index对象是不可改变的。
Index对象常用的方法:
method | comment |
---|---|
.append(idx) | 链接另一个Index对象,产生新的Index对象 |
.diff(idx) | 计算差集,产生新的Index对象 |
.intersection(idx) | 计算交集 |
.union(idx) | 计算并集 |
.delete(loc) | 删除loc位置处的元素 |
.insert(loc, e) | 在loc位置增加一个元素e |
这里在跑课程中的实例时出现了问题:
In [36]: nc = d.columns.delete(2)
In [37]: ni = d.index.insert(5, 'c0')
In [38]: nd = d.reindex(index=ni, columns=nc, method='ffill')
#这一句会报错,错误原因是索引不是单调的,所以填充失败。经查资料,该问题暂时无法解决,可能是因为删除引起的,只好先删除,再调用填充方法。
In [39]: nc = d.columns.delete(2)
In [40]: ni = d.index.insert(5, 'c0')
In [41]: nd = d.reindex(index=ni, columns=nc)
In [42]: nd.ffill()
Out[42]:
城市 环比 定基
c5 沈阳 100.1 101.6
c4 深圳 102.0 145.5
c3 广州 101.3 120.0
c2 上海 101.2 127.8
c1 北京 101.5 121.4
c0 北京 101.5 121.4
.drop
In [53]: a
Out[53]:
a 9
b 8
c 7
d 6
dtype: int64
In [54]: a.drop(['b' ,'c'])
Out[54]:
a 9
d 6
dtype: int64
In [61]: d
Out[61]:
城市 环比 同比 定基
c5 沈阳 100.1 101.4 101.6
c4 深圳 102.0 140.9 145.5
c3 广州 101.3 119.4 120.0
c2 上海 101.2 127.3 127.8
c1 北京 101.5 120.7 121.4
In [62]: d.drop('c5')
Out[62]:
城市 环比 同比 定基
c4 深圳 102.0 140.9 145.5
c3 广州 101.3 119.4 120.0
c2 上海 101.2 127.3 127.8
c1 北京 101.5 120.7 121.4
In [63]: d.drop('同比', axis=1)#注意:这里要指定axis,否则会认为删除的是0轴上的元素
Out[63]:
城市 环比 定基
c5 沈阳 100.1 101.6
c4 深圳 102.0 145.5
c3 广州 101.3 120.0
c2 上海 101.2 127.8
c1 北京 101.5 121.4
广播:
In [107]: a = np.arange(10, 20).reshape(2, 5)
In [108]: b = np.arange(22, 32).reshape(2, 5)
In [109]: a + b
Out[109]:
array([[32, 34, 36, 38, 40],
[42, 44, 46, 48, 50]])
In [110]: a + 1.0/b
Out[110]:
array([[10.04545455, 11.04347826, 12.04166667, 13.04 , 14.03846154],
[15.03703704, 16.03571429, 17.03448276, 18.03333333, 19.03225806]])
#这里1.0/b就用到了广播
In [112]: a = np.arange(0, 10).reshape(2, 5)
In [113]: 2*a
Out[113]:
array([[ 0, 2, 4, 6, 8],
[10, 12, 14, 16, 18]])
#这里也用到了广播
广播是指在numpy数组间,就算不同形也可以计算的性质,在Pandas中DataFrame和Series也有类似的性质。当然可以广播进行广播是有前提的,要符合广播机制。
若两个数组维度不同,但是后缘维度的轴长相同,或者其中有一个轴长为1,则广播兼容
后缘维度:从数组的shape元组后面数就叫后缘维度。
In [112]: a = np.arange(0, 10).reshape(2, 5)
In [113]: 2*a
Out[113]:
array([[ 0, 2, 4, 6, 8],
[10, 12, 14, 16, 18]])
#后缘维度为5
In [116]: b = np.arange(10)
In [117]: b.shape
Out[117]: (10,)
#后缘维度10
后缘维度相同:
In [121]: arr1 = np.array([[0, 0, 0], [1, 1, 1,], [2, 2, 2], [3, 3, 3]])
In [122]: arr2 = np.array([1, 2, 3])
In [123]: print(arr1.shape, arr2.shape)
(4, 3) (3,)
In [124]: arr_sum = arr1 + arr2
In [125]: arr_sum
Out[125]:
array([[1, 2, 3],
[2, 3, 4],
[3, 4, 5],
[4, 5, 6]])
\begin{bmatrix}
1 & 2 & 3\
2 & 3 & 4\
3 & 4 & 5\
4 & 5 & 6
\end{bmatrix}
$$
后缘维度中有1:
In [130]: arr1 = np.array([[0, 0, 0], [1, 1, 1,], [2, 2, 2,], [3, 3, 3]])
In [131]: arr2 = np.array([[1], [2], [3], [4]])
In [132]: print(arr1.shape, arr2.shape)
(4, 3) (4, 1)
In [133]: arr1+arr2
Out[133]:
array([[1, 1, 1],
[3, 3, 3],
[5, 5, 5],
[7, 7, 7]])
\begin{bmatrix}
1 & 1 & 1\
3 & 3 & 3\
5 & 5 & 5\
7 & 7 & 7
\end{bmatrix}
$$
算数运算:
In [135]: a = pd.DataFrame(np.arange(12).reshape(3, 4))
In [136]: b = pd.DataFrame(np.arange(20).reshape(4, 5))
In [137]: a
Out[137]:
0 1 2 3
0 0 1 2 3
1 4 5 6 7
2 8 9 10 11
In [138]: b
Out[138]:
0 1 2 3 4
0 0 1 2 3 4
1 5 6 7 8 9
2 10 11 12 13 14
3 15 16 17 18 19
In [139]: a + b
Out[139]:
0 1 2 3 4
0 0.0 2.0 4.0 6.0 NaN
1 9.0 11.0 13.0 15.0 NaN
2 18.0 20.0 22.0 24.0 NaN
3 NaN NaN NaN NaN NaN
In [140]: a * b
Out[140]:
0 1 2 3 4
0 0.0 1.0 4.0 9.0 NaN
1 20.0 30.0 42.0 56.0 NaN
2 80.0 99.0 120.0 143.0 NaN
3 NaN NaN NaN NaN NaN
方法形式:
method | comment |
---|---|
.add(d, **argws) | 加法,带可选参数 |
.sub(d, **argws) | 减肥,带可选参数 |
.mul(d, **argws) | 乘法,带可选参数 |
.div(d, **argws) | 除法,带可选参数 |
这种形式相对于符号运算形式是有优点的,可以看到符号形式在自动补齐的地方全是NaN,这种可以指定填充字符:
In [141]: b.add(a, fill_value=100)
Out[141]:
0 1 2 3 4
0 0.0 2.0 4.0 6.0 104.0
1 9.0 11.0 13.0 15.0 109.0
2 18.0 20.0 22.0 24.0 114.0
3 115.0 116.0 117.0 118.0 119.0
In [142]: a.mul(b, fill_value=0)
Out[142]:
0 1 2 3 4
0 0.0 1.0 4.0 9.0 0.0
1 20.0 30.0 42.0 56.0 0.0
2 80.0 99.0 120.0 143.0 0.0
3 0.0 0.0 0.0 0.0 0.0
#使用fill_value补齐数值
不同维度(类型)的运算
注意Pandas的广播机制和numpy不同
默认在1轴广播,不同维也可广播,填充NaN
In [160]: b
Out[160]:
0 1 2 3 4
0 0 1 2 3 4
1 5 6 7 8 9
2 10 11 12 13 14
3 15 16 17 18 19
In [161]: c
Out[161]:
0 0
1 1
2 2
3 3
dtype: int64
In [162]: b - c
Out[162]:
0 1 2 3 4
0 0.0 0.0 0.0 0.0 NaN
1 5.0 5.0 5.0 5.0 NaN
2 10.0 10.0 10.0 10.0 NaN
3 15.0 15.0 15.0 15.0 NaN
可以指定0轴广播:
In [164]: b.sub(c, axis=0)
Out[164]:
0 1 2 3 4
0 0 1 2 3 4
1 4 5 6 7 8
2 8 9 10 11 12
3 12 13 14 15 16
比较运算,产生bool对象:
In [165]: a = pd.DataFrame(np.arange(12).reshape(3, 4))
In [166]: a
Out[166]:
0 1 2 3
0 0 1 2 3
1 4 5 6 7
2 8 9 10 11
In [167]: d = pd.DataFrame(np.arange(12, 0, -1).reshape(3, 4))
In [168]: d
Out[168]:
0 1 2 3
0 12 11 10 9
1 8 7 6 5
2 4 3 2 1
In [169]: a > d
Out[169]:
0 1 2 3
0 False False False False
1 False False False True
2 True True True True
In [170]: a == d
Out[170]:
0 1 2 3
0 False False False False
1 False False True False
2 False False False False
比较运算与算数运算不同的是,比较运算只能比较相同索引的元素,不进行补齐。
二维一维,一维零维间为广播运算。使用符号进行的比较运算产生bool对象。
In [179]: a = pd.DataFrame(np.arange(12).reshape(3, 4))
In [180]: a
Out[180]:
0 1 2 3
0 0 1 2 3
1 4 5 6 7
2 8 9 10 11
In [181]: c = pd.Series(np.arange(4))
In [182]: c
Out[182]:
0 0
1 1
2 2
3 3
dtype: int64
In [183]: a > c
Out[183]:
0 1 2 3
0 False False False False
1 True True True True
2 True True True True
In [184]: c > 0
Out[184]:
0 False
1 True
2 True
3 True
dtype: bool
#1轴广播运算,与0维数的运算
看到这里,我觉得目前没必要继续下去了,要回归主线AI了。接下俩可能会看看Pytorch,OpenCV,接着看学了一半的C++,并且开个矩阵论和自动驾驶原理的头。