Python数据分析学习笔记

Pandas

2018-09-13  本文已影响0人  Alex_杨策

Pandas建立在NumPy基础上,处理二维数据更加得心应手。

Series和DataFrame

Series和DataFrame是Pandas中的两种核心数据结构,大部分Pandas的功能都围绕着两种这两种数据结构进行。

Series是值得序列,可以理解为一维数组,只有一个列和索引。索引可以定制,当不指定时默认使用整数索引,而且索引可以被命名:

In [1]: import pandas as pd
In [2]: import numpy as np
In [4]: from pandas import Series, DataFrame

In [5]: s1 = Series([1, 2, 3, 4, 5])

In [6]: s1
Out[6]:
0    1
1    2
2    3
3    4
4    5
dtype: int64

In [7]: s2 = Series([1, 2, 3, 4, 5], index=['a', 'b', 'c', 'd', 'e' ])

In [8]: s2
Out[8]:
a    1
b    2
c    3
d    4
e    5
dtype: int64

In [9]: s2.index.name = 'index'

In [10]: s2.index
Out[10]: Index(['a', 'b', 'c', 'd', 'e'], dtype='object', name='index')

DataFrame类似于二维数组,有行和列之分,除了像Series一样,多个行有索引之外,每个列上面还可以有标签label,索引和标签本身都可以被命名:

In [14]: df = DataFrame(np.random.randn(4, 4), index=['a', 'b', 'c', 'd'], columns=['A', 'B', 'C', 'D'])

In [15]: df
Out[15]:
          A         B         C         D
a  0.311412  0.063509  1.195284 -0.921551
b  0.906860 -1.183861 -0.002180 -2.067180
c -0.121993  0.494496  0.870654  0.852347
d -0.461892  0.784124  0.069672 -0.813653

In [16]: df.index
Out[16]: Index(['a', 'b', 'c', 'd'], dtype='object')

In [17]: df.columns
Out[17]: Index(['A', 'B', 'C', 'D'], dtype='object')

上面得代码,通过指定索引和标签(columns参数)创建了一个DataFrame实例。可以通过df.index和df.columns分别访问索引和标签。

选择

对于Series数据来说,主要通过其索引来进行选择:

In [18]: s2 = Series([1, 2, 3, 4, 5], index=['a', 'b', 'c', 'd', 'e'])

In [19]: s2
Out[19]:
a    1
b    2
c    3
d    4
e    5
dtype: int64

In [20]: s2[0]
Out[20]: 1

In [21]: s2['a']
Out[21]: 1

In [22]: s2[0:3]
Out[22]:
a    1
b    2
c    3
dtype: int64

In [23]: s2['a':'c']
Out[23]:
a    1
b    2
c    3
dtype: int64

对于指定了索引的Series序列来说,有两种选择元素的方式,一种是整数索引,(说明整数索引一直默认存在),第二种方式是通过指定字符索引进行。整数索引和字符索引分别调用了s2.iloc和s2.loc索引,其中iloc代表整数索引:

In [24]: s2.iloc[0:3]
Out[24]:
a    1
b    2
c    3
dtype: int64

In [25]: s2.loc['a':'c']
Out[25]:
a    1
b    2
c    3
dtype: int64

对于DateFrame数据,由于有行列之分,可以有多种选择数据的方式,可以通过索引和标签来选择,对于标签(列)选择:

In [27]: df.A
Out[27]:
a    0.311412
b    0.906860
c   -0.121993
d   -0.461892
Name: A, dtype: float64
In [29]: df['A']
Out[29]:
a    0.311412
b    0.906860
c   -0.121993
d   -0.461892
Name: A, dtype: float64

对于标签(列),可以通过df.A的属性方式,或者通过df['A']来访问。如果要选择多列,可以通过df.columns来获取所有列标签,然后选择部分标签来选择df的多列数据。

In [30]: df[df.columns[0:2]]
Out[30]:
          A         B
a  0.311412  0.063509
b  0.906860 -1.183861
c -0.121993  0.494496
d -0.461892  0.784124

可以用loc和iloc属性来选择某一行

In [33]: df
Out[33]:
          A         B         C         D
a  0.311412  0.063509  1.195284 -0.921551
b  0.906860 -1.183861 -0.002180 -2.067180
c -0.121993  0.494496  0.870654  0.852347
d -0.461892  0.784124  0.069672 -0.813653

In [34]: df.loc['a']
Out[34]:
A    0.311412
B    0.063509
C    1.195284
D   -0.921551
Name: a, dtype: float64

In [35]: df.loc['a':'b']
Out[35]:
          A         B         C         D
a  0.311412  0.063509  1.195284 -0.921551
b  0.906860 -1.183861 -0.002180 -2.067180

In [36]: df.iloc[0]
Out[36]:
A    0.311412
B    0.063509
C    1.195284
D   -0.921551
Name: a, dtype: float64

In [37]: df.iloc[0:2]
Out[37]:
          A         B         C         D
a  0.311412  0.063509  1.195284 -0.921551
b  0.906860 -1.183861 -0.002180 -2.067180

有了行选择方式就有了更多方法来选择列:

In [38]: df.loc[:,['B', 'C', 'D']]
Out[38]:
          B         C         D
a  0.063509  1.195284 -0.921551
b -1.183861 -0.002180 -2.067180
c  0.494496  0.870654  0.852347
d  0.784124  0.069672 -0.813653

df.loc支持二维选择,可以同时选择行和列,:符号代表选择所有的行。可以同时选择具体的某一行或者某一列:

In [39]: df
Out[39]:
          A         B         C         D
a  0.311412  0.063509  1.195284 -0.921551
b  0.906860 -1.183861 -0.002180 -2.067180
c -0.121993  0.494496  0.870654  0.852347
d -0.461892  0.784124  0.069672 -0.813653

In [40]: df.loc['a', 'A']
Out[40]: 0.31141203738121559

In [41]: df.loc['b':'c', 'B':'C']
Out[41]:
          B         C
b -1.183861 -0.002180
c  0.494496  0.870654

缺失值和数据自动对齐

Pandas中最重要的一个功能是,它可以对不同索引的对象进行算术运算。比如对两个Series数据进行相加时,如果存在不用的索引,则结果时两个索引的并集:

In [42]: s1 = Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])

In [43]: s1
Out[43]:
a    1
b    2
c    3
d    4
dtype: int64

In [44]: s2 = Series([2, 3, 4, 5], index=['b', 'c', 'd', 'e'])

In [45]: s2
Out[45]:
b    2
c    3
d    4
e    5
dtype: int64

In [46]: s1 + s2
Out[46]:
a    NaN
b    4.0
c    6.0
d    8.0
e    NaN
dtype: float64

以上代码中创建了s1和s2两个序列,具有相同的索引['b', 'c', 'd'],所以在相加时,相同索引的值会相加,但不重叠的索引会引入NaN值,也就是缺失值。缺失值会在运算中传播,所以最终结果也是NaN值。
根据相同索引进行自动计算,就是自动对齐功能。同样的规则也适用于DataFrame:

In [49]: df1 = DataFrame(np.arange(9).reshape(3, 3), columns=list('ABC'), index=list('abc'
    ...: ))

In [50]: df2 = DataFrame(np.arange(12).reshape(3, 4), columns=list('ABDE'), index=list('bc
    ...: d'))

In [51]: df1
Out[51]:
   A  B  C
a  0  1  2
b  3  4  5
c  6  7  8

In [52]: df2
Out[52]:
   A  B   D   E
b  0  1   2   3
c  4  5   6   7
d  8  9  10  11

In [53]: df1 + df2
Out[53]:
      A     B   C   D   E
a   NaN   NaN NaN NaN NaN
b   3.0   5.0 NaN NaN NaN
c  10.0  12.0 NaN NaN NaN
d   NaN   NaN NaN NaN NaN

在计算时可以指定使用的值来填充NaN值,然后带入计算过程:

In [54]: df1.add(df2, fill_value=0)
Out[54]:
      A     B    C     D     E
a   0.0   1.0  2.0   NaN   NaN
b   3.0   5.0  5.0   2.0   3.0
c  10.0  12.0  8.0   6.0   7.0
d   8.0   9.0  NaN  10.0  11.0

指定了使用0来填充NaN值,然后带入计算过程,注意这里填充的不是最终的计算结果。可以看到依然有元素为NoN值,是因为这个位置的元素在df1和df2中都未定义。

常用运算

Series和DataFrame的常用运算方式和NumPy中的差不多。在Pandas中还有一种比较常见的操作是将函数应用到每行或者每列上面,DataFrame的apply方法可以实现此功能,比如想统计每行和每列的极差(最大值和最小值之差):

In [55]: df1 = DataFrame(np.arange(9).reshape(3, 3), columns=list('ABC'), index=list('abc'
    ...: ))

In [56]: df1
Out[56]:
   A  B  C
a  0  1  2
b  3  4  5
c  6  7  8

In [57]: f = lambda x: x.max() - x.min()

In [58]: df1.apply(f)
Out[58]:
A    6
B    6
C    6
dtype: int64

In [59]: df1.apply(f, axis=1)
Out[59]:
a    2
b    2
c    2
dtype: int64

以上代码中,定义了f匿名函数,通过df.apply(f)应用f,统计出每列的极差,接着通过传入 axis=1 参数,统计出每行的极差。
如果想把函数应用到每一个元素上,DateFrame数据可以使用df.applymap方法,Series可以使用s.map方法:

In [60]: df1.applymap(lambda x: x+1)
Out[60]:
   A  B  C
a  1  2  3
b  4  5  6
c  7  8  9

以上代码中,使所有元素都自增加1,其结果和 df1+1的运算结果相同。

常用统计

类似于NumPy, Series和DataFrame也有各种统计方法,比如求平均值,方差,和等方法,同时通过describe方法可以得到当前数据的一些常用的统计信息:

In [61]: df1 = DataFrame(np.arange(9).reshape(3,3), columns=list('ABC'), index=list('abc')
    ...: )

In [62]: df1
Out[62]:
   A  B  C
a  0  1  2
b  3  4  5
c  6  7  8

In [63]: df1.sum()
Out[63]:
A     9
B    12
C    15
dtype: int64

In [64]: df1.mean()
Out[64]:
A    3.0
B    4.0
C    5.0
dtype: float64

In [65]: df1.sum(axis=1)
Out[65]:
a     3
b    12
c    21
dtype: int64

In [66]: df1.describe()
Out[66]:
         A    B    C
count  3.0  3.0  3.0
mean   3.0  4.0  5.0
std    3.0  3.0  3.0
min    0.0  1.0  2.0
25%    1.5  2.5  3.5
50%    3.0  4.0  5.0
75%    4.5  5.5  6.5
max    6.0  7.0  8.0

对于这些统计函数,我们可以指定统计的纬度,和NumPy多维数组的统计函数十分相似。默认按列统计,也可以通过参数axis=1指定按行统计。
describe方法显示了一些常用的统计信息:

- 'conut' 元素值得数量:
- 'mean' 平均值:
- 'std' 标准差:
- 'min' 最小值:
- '25%' 下四分位数:
- '50%' 中位数:
- '75%' 上四分位数:
- 'max' 最大值:

数据合并和分组

有时候需要合并两个DataFrame数据,合并数据得方式有两种,一种简单得进行拼接,另一种是根据列名类像数据库查询一样进行合并。这两种方法分别通过pandas.concat和pandas.merge方法实现:

In [67]: df1 = DataFrame(np.random.randn(3, 3))
In [69]: df2 = DataFrame(np.random.randn(3, 3), index=[5, 6, 7])

In [70]: df1
Out[70]:
          0         1         2
0 -0.975353  0.516803 -0.784033
1  0.243648 -0.671816 -1.232789
2  0.433984  1.175839 -2.421708

In [71]: df2
Out[71]:
          0         1         2
5 -1.261180  0.043069  0.301943
6  0.747142 -0.113258 -0.700924
7 -0.687073  0.834874  0.790432

In [72]: pd.concat([df1, df2])
Out[72]:
          0         1         2
0 -0.975353  0.516803 -0.784033
1  0.243648 -0.671816 -1.232789
2  0.433984  1.175839 -2.421708
5 -1.261180  0.043069  0.301943
6  0.747142 -0.113258 -0.700924
7 -0.687073  0.834874  0.790432

有的时候,有多个数据集,这些数据集有相同的列,就可以按照这个列进行合并操作,类似于数据库中的join操作:

In [73]: df1 = DataFrame({'user_id': [5348, 13], 'course': [12, 45], 'minutes': [9, 36]})

In [74]: df2 = DataFrame({'course': [12, 45], 'name': ['Linux基础入门', '数据分析']})

In [75]: df1
Out[75]:
   course  minutes  user_id
0      12        9     5348
1      45       36       13

In [76]: df2
Out[76]:
   course       name
0      12  Linux基础入门
1      45       数据分析

In [77]: pd.merge(df1, df2)
Out[77]:
   course  minutes  user_id       name
0      12        9     5348  Linux基础入门
1      45       36       13       数据分析

可以看到当通过字段创建DataFrame数据集时,键变成了列名。df1和df2有共同的列course,当进行merge操作的时候Pandas会自动安装这列进行合并。

在Pandas中,也支持类似数据库查询语句GROUP BY的功能,也就是按照某列进行分组,然后在分组上进行一些计算操作,有如下数据集,计算其中user_id为5348的用户的学习时间:

In [79]: df = DataFrame({'user_id': [5348, 13, 5348], 'course': [12, 45, 23], 'minutes': [
    ...: 9, 36, 45]})

In [80]: df
Out[80]:
   course  minutes  user_id
0      12        9     5348
1      45       36       13
2      23       45     5348

一种办法时筛选出所有user_id为5348的行,然后进行求和统计:

In [81]: df[df['user_id'] == 5348]
Out[81]:
   course  minutes  user_id
0      12        9     5348
2      23       45     5348

In [82]: df[df['user_id'] == 5348]['minutes']
Out[82]:
0     9
2    45
Name: minutes, dtype: int64

In [83]: df[df['user_id'] == 5348]['minutes'].sum()
Out[83]: 54

也可以使用类似于数据库的 GROUP BY功能进行计算:

In [84]: df[['user_id', 'minutes']]
Out[84]:
   user_id  minutes
0     5348        9
1       13       36
2     5348       45

In [85]: df[['user_id', 'minutes']].groupby('user_id').sum()
Out[85]:
         minutes
user_id
13            36
5348          54

相对于第一种方式,通过groupby方法在user_id上进行分组并求和,相对于前一种方式,分组求和更加灵活一些。

时间序列处理

要分析的数据中,很多数据都带有时间戳,用Pandas根据时间戳进行计算很方便。
创建一个简单的时间序列:

from datetime import datetime
In [88]: dates = [datetime(2018, 1, 1), datetime(2018, 1, 2), datetime(2018, 1, 3), dateti
    ...: me(2018, 1, 4)]

In [89]: ts = Series(np.random.randn(4), index=dates)

In [90]: ts
Out[90]:
2018-01-01    0.885089
2018-01-02    0.114941
2018-01-03   -0.831710
2018-01-04    0.382641
dtype: float64

In [91]: ts.index
Out[91]: DatetimeIndex(['2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04'], dtype='datetime64[ns]', freq=None)

In [92]: ts.index[0]
Out[92]: Timestamp('2018-01-01 00:00:00')

通过datetime时间类型生成了一堆时间,然后基于此创建了一个时间序列ts,ts的索引类型为DatetimeIndex类型。生成ts以后我们就有了多种发放选择元素了,只需要传入一个能被Pandas识别的日期字符串就可以:

In [92]: ts.index[0]
Out[92]: Timestamp('2018-01-01 00:00:00')

In [93]: ts[ts.index[0]]
Out[93]: 0.8850886421792693

In [94]: ts['2018-01-01']
Out[94]: 0.8850886421792693

In [95]: ts['2018/01/01']
Out[95]: 0.8850886421792693

In [96]: ts['1/1/2018']
Out[96]: 0.8850886421792693

In [97]: ts[datetime(2018, 1, 1)]
Out[97]: 0.8850886421792693

在Pandas中生成日期范围也非常灵活,主要通过pandas.date_range函数完成,该函数主要有以下几个参数:

In [98]: pd.date_range('2018-1-1', '2019', freq='M')
Out[98]:
DatetimeIndex(['2018-01-31', '2018-02-28', '2018-03-31', '2018-04-30',
               '2018-05-31', '2018-06-30', '2018-07-31', '2018-08-31',
               '2018-09-30', '2018-10-31', '2018-11-30', '2018-12-31'],
              dtype='datetime64[ns]', freq='M')

In [99]: pd.date_range('2018-1-1', '2018-12-1', freq='MS')
Out[99]:
DatetimeIndex(['2018-01-01', '2018-02-01', '2018-03-01', '2018-04-01',
               '2018-05-01', '2018-06-01', '2018-07-01', '2018-08-01',
               '2018-09-01', '2018-10-01', '2018-11-01', '2018-12-01'],
              dtype='datetime64[ns]', freq='MS')

有时候时间序列是按每小时显示统计的,但是我们想将统计频率转换成按天来统计,可以使用时间序列的resample方法,该方法非常强大,不仅仅支持高频率的数据聚合到低频率(降采样),也支持低频率转化到高频率统计(升采样):

In [100]: dates = pd.date_range('2018-1-1', '2018-1-2 23:00:00', freq='H')

In [101]: ts = Series(np.arange(len(dates)), index=dates)

In [102]: ts.size
Out[102]: 48

In [103]: ts.head(5)
Out[103]:
2018-01-01 00:00:00    0
2018-01-01 01:00:00    1
2018-01-01 02:00:00    2
2018-01-01 03:00:00    3
2018-01-01 04:00:00    4
Freq: H, dtype: int32

In [104]: ts.tail(5)
Out[104]:
2018-01-02 19:00:00    43
2018-01-02 20:00:00    44
2018-01-02 21:00:00    45
2018-01-02 22:00:00    46
2018-01-02 23:00:00    47
Freq: H, dtype: int32

以上代码,创建了以每小时为频率的时间序列,该数据集有48个元素,使用ts.head(5)方法显示前5个元素,使用ts.tail(5)显示后5个元素。
把以上数据转换为按天数据统计:

In [107]: ts.resample('D').sum()
Out[107]:
2018-01-01    276
2018-01-02    852
Freq: D, dtype: int32

也可以按天的所有数据的平均数进行统计:

In [108]: ts.resample('D').mean()
Out[108]:
2018-01-01    11.5
2018-01-02    35.5
Freq: D, dtype: float64

当把低频率的数据转换成高频率的数据时,默认情况下Pandas会引入NoN值,因为没有办法从低频率的数据计算出高频率的数据,但可以通过fill_method参数指定插值方式:

In [109]: ts.resample('D').mean().resample('H').mean()
Out[109]:
2018-01-01 00:00:00    11.5
2018-01-01 01:00:00     NaN
2018-01-01 02:00:00     NaN
....
2018-01-01 21:00:00     NaN
2018-01-01 22:00:00     NaN
2018-01-01 23:00:00     NaN
2018-01-02 00:00:00    35.5
Freq: H, dtype: float64

以上代码,我们先将ts转换为按天统计,接着又转换成按小时平均值统计,Pandas引入NaN值,但当使用ffill(用前面的值代替NaN值)就不会又NaN值出现:

In [110]: ts.resample('D').mean().resample('H').ffill()
Out[110]:
2018-01-01 00:00:00    11.5
2018-01-01 01:00:00    11.5
....
2018-01-01 23:00:00    11.5
2018-01-02 00:00:00    35.5
Freq: H, dtype: float64

Pandas总结

Pandas虽然构建于NumPy之上,但提供了更灵活强大的功能:

上一篇下一篇

猜你喜欢

热点阅读