Pandas介绍

1. 安装

首先,需要先安装numpy和pandas环境,参考:https://pandas.pydata.org/
。以下语句检查并确认安装成功。

import numpy as np 
import pandas as pd
pd.__version__
pd?

2. Pandas对象

Pandas 有三种基本数据结构:Series、DataFrame 和 Index。

2.1. Series对象

Pandas 的 Series 对象是一个带索引数据构成的一维数组。Series 对象将一组数据和一组索引绑定在一起,我们可以通过 values 属性和 index 属性获取数据。values 属性返回的结果与 NumPy 数组类似;index 属性返回的结果是一个类型为 pd.Index 的类数组对象。
可以像访问Numpy那样来访问Series(序号也是从0开始计数的)。

In [1]: import numpy as py

In [2]: import pandas as pd

In [3]: data = pd.Series([4.57,92,783.23,134,5.6])

In [4]: data
Out[4]: 
0      4.57
1     92.00
2    783.23
3    134.00
4      5.60
dtype: float64

In [5]: data.values
Out[5]: array([  4.57,  92.  , 783.23, 134.  ,   5.6 ])

In [6]: data.index
Out[6]: RangeIndex(start=0, stop=5, step=1)

In [7]: data[2:]
Out[7]: 
2    783.23
3    134.00
4      5.60
dtype: float64

In [8]: 

Pandas 的 Series 对象比Numpy更加灵活、通用。
两者的主要区别是:NumPy 数组通过隐式定义的整数索引获取数值,而 Pandas 的 Series 对象用一种显式定义的索引与数值关联。也就是说,Numpy的索引是系统自分配的无法更改,但是Series对象是可以手工指定的。

In [9]: ds1 = pd.Series([2,4,6,8], index=['f','i','b','a'])

In [10]: ds1
Out[10]: 
f    2
i    4
b    6
a    8
dtype: int64

In [11]: ds1['b']
Out[11]: 6

In [12]: ds2 = pd.Series([1.4,1.8,2.2,2.6],index=[4,7,3,1])

In [13]: ds2
Out[13]: 
4    1.4
7    1.8
3    2.2
1    2.6
dtype: float64

Series是特殊的字典,Series 对象其实是一种将类型键映射到一组类型值的数据结构,Pandas Series 的类型信息使得它在某些操作上比 Python 的字典更高效。用字典创建 Series 对象时,其索引默认按照顺序排列

In [14]: city_code = {'Beijing':'010','Tianjin':'022','Guangzhou':'020',
'Dalian':'0411','Shanghai':'021','Changchun':'0431'}

In [15]: city_se = pd.Series(city_code)

In [16]: city_se
Out[16]: 
Beijing       010
Changchun    0431
Dalian       0411
Guangzhou     020
Shanghai      021
Tianjin       022
dtype: object

In [17]: city_se['Dalian']
Out[17]: '0411'

In [18]: city_se['Beijing':'Guangzhou']
Out[18]: 
Beijing       010
Changchun    0431
Dalian       0411
Guangzhou     020
dtype: object

2.2. DataFrame对象

DataFrame类似于RDBMS中的Table。DataFrame就可以看作是一种既有灵活的行索引,又有灵活列名的二维数组。
DataFrame有2个常用属性,分别是index 属性columns 属性。前者可以获取索引标签(行标签);后者是是存放列标签的Index 对象。DataFrame 是特殊的字典,一列映射一个Series 的数据。
DataFrame可以通过以下几种方式来创建:(1)通过单个 Series 对象创建。(2)通过字典列表创建。(3)通过 Series 对象字典创建。(4)通过NumPy 二维数组创建。(5)通过 NumPy 结构化数组创建。

In [3]: order_count = {'nanjing':201614, 'shanghai':266105, 'hangzhou':21477, 
'zhengzhou':11275 , 'beijing':2271}

In [4]: price = {'nanjing':201614, 'shanghai':266105, 'hangzhou':21477, 
'zhengzhou':11275 , 'beijing':2271}

In [5]: order_count_se = pd.Series(order_count)

In [6]: price_se = pd.Series(price)

In [7]: df1 = pd.DataFrame({'order_cnt': order_count_se , 'price':price_se })

In [8]: df1.index
Out[8]: Index([u'beijing', u'hangzhou', u'nanjing', u'shanghai',
 u'zhengzhou'], dtype='object')

In [9]: df1.columns
Out[9]: Index([u'order_cnt', u'price'], dtype='object')

In [10]: df1
Out[10]: 
           order_cnt   price
beijing         2271    2271
hangzhou       21477   21477
nanjing       201614  201614
shanghai      266105  266105
zhengzhou      11275   11275

In [11]: df2 = pd.DataFrame(order_count_se, columns = ['order_count'])

In [12]: df2
Out[12]: 
           order_count
beijing           2271
hangzhou         21477
nanjing         201614
shanghai        266105
zhengzhou        11275

In [13]: df3 = pd.DataFrame([ {'a' : i , 'b' : 3*i} for i in range(4) ])

In [14]: df3
Out[14]: 
   a  b
0  0  0
1  1  3
2  2  6
3  3  9

In [15]: df4 = pd.DataFrame(np.random.rand(3,2),
columns=['A','B'],index=['a','b','c']) 

In [16]: df4
Out[16]: 
          A         B
a  0.830527  0.925459
b  0.210633  0.806638
c  0.604675  0.222498

2.3. Index对象

可以将Index视为一个不可变数组或有序集合。当作为不可变数组时,一般数组的访问方式(例如切片等)对Index适用,与数组的最大区别是Index对象不可更改。当作为集合时,Index也可以做交集、并集等常规操作。

In [21]: ind1 = pd.Index([1,2,3,5,18,27])

In [22]: ind2 = pd.Index([2,3,7,18,56,345])

In [23]: ind1[3]
Out[23]: 5

In [24]: ind1[::2]
Out[24]: Int64Index([1, 3, 18], dtype='int64')

In [25]: ind1[2] = 8 
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
 in ()
----> 1 ind1[2] = 8

/Users/natty/anaconda2/lib/python2.7/site-packages/pandas/core/indexes/base.pyc 
in __setitem__(self, key, value)
   2063 
   2064     def __setitem__(self, key, value):
-> 2065         raise TypeError("Index does not support mutable operations")
   2066 
   2067     def __getitem__(self, key):

TypeError: Index does not support mutable operations

In [26]: ind1 & ind2
Out[26]: Int64Index([2, 3, 18], dtype='int64')

In [27]: ind1 | ind2
Out[27]: Int64Index([1, 2, 3, 5, 7, 18, 27, 56, 345], dtype='int64')

In [28]: ind1 ^ ind2
Out[28]: Int64Index([1, 5, 7, 27, 56, 345], dtype='int64')

3. 数据取值和访问

3.1. Series数值访问

Series的访问既可以作为字典,也可以作为一维数组。数据访问的方法,可以参考Numpy的访问方式,这里不赘述。

In [32]: list(order_count_se.items())
Out[32]: 
[('beijing', 2271),
 ('hangzhou', 21477),
 ('nanjing', 201614),
 ('shanghai', 266105),
 ('zhengzhou', 11275)]

In [33]: order_count_se.keys()
Out[33]: Index([u'beijing', u'hangzhou', u'nanjing', u'shanghai', 
u'zhengzhou'], dtype='object')

In [36]: 'shanghai' in order_count_se   # 验证key是否在Series中
Out[36]: True

In [37]: order_count_se['tianjin'] = 5690  #可以增加键值对方式修改Series

In [38]: order_count_se
Out[38]: 
beijing        2271
hangzhou      21477
nanjing      201614
shanghai     266105
zhengzhou     11275
tianjin        5690
dtype: int64

3.2. 隐式索引和显式索引

如果Series的显式索引是整数,那么在访问时,很容易混淆。例如下边的例子:

In [3]: se_001 = pd.Series(['e','a','z','x','f','c','d'] , index = [1,3,5,4,2,7,6])

In [4]: se_001
Out[4]: 
1    e
3    a
5    z
4    x
2    f
7    c
6    d
dtype: object

In [5]: se_001[5]
Out[5]: 'z'

In [6]: se_001[2:4]
Out[6]: 
5    z
4    x
dtype: object

从上边的例子总结得出,python的默认规则是:在单个访问时,使用的显式索引,而在切片时,使用的是隐式索引,很容易混淆!python提供了loc、iloc和ix三种索引器。
loc表示:表示取值和切片都是显式的。iloc 属性,表示取值和切片都是隐式索引。ix是loc和iloc的混合形式,应用于dataFrame(使用例子在3.3节)。

In [7]: se_001.loc[5]
Out[7]: 'z'

In [8]: se_001.loc[2:4]
Out[8]: Series([], dtype: object)

In [14]: se_001.iloc[5]
Out[14]: 'c'

In [15]: se_001.iloc[2:4]
Out[15]: 
5    z
4    x
dtype: object

3.3. DataFrame读取

dataframe可以通过对列名进行字典形式(dictionary-style)的取值获取数据。可以把 DataFrame 看成是一个增强版的二维数组,用 values 属性按行查看数组数据。ix 索引器对于整数索引的处理和之前在 Series 对象中介绍的一样,都容易让人混淆。

In [9]: df1
Out[9]: 
           order_cnt      price
beijing         2271       6984
hangzhou       21477     103019
nanjing       201614  490291034
shanghai      266105  786594932
zhengzhou      11275      93329

In [10]: df1['price']
Out[10]: 
beijing           6984
hangzhou        103019
nanjing      490291034
shanghai     786594932
zhengzhou        93329
Name: price, dtype: int64

In [11]: df1['price_per_or'] = df1['price']/df1['order_cnt']

In [12]: df1
Out[12]: 
           order_cnt      price  price_per_or
beijing         2271       6984      3.075297
hangzhou       21477     103019      4.796713
nanjing       201614  490291034   2431.830299
shanghai      266105  786594932   2955.956979
zhengzhou      11275      93329      8.277517

In [13]: df1.values
Out[13]: 
array([[2.27100000e+03, 6.98400000e+03, 3.07529723e+00],
       [2.14770000e+04, 1.03019000e+05, 4.79671276e+00],
       [2.01614000e+05, 4.90291034e+08, 2.43183030e+03],
       [2.66105000e+05, 7.86594932e+08, 2.95595698e+03],
       [1.12750000e+04, 9.33290000e+04, 8.27751663e+00]])

In [14]: df1.T
Out[14]: 
                  beijing       hangzhou       nanjing      shanghai     zhengzhou
order_cnt     2271.000000   21477.000000  2.016140e+05  2.661050e+05  11275.000000
price         6984.000000  103019.000000  4.902910e+08  7.865949e+08  93329.000000
price_per_or     3.075297       4.796713  2.431830e+03  2.955957e+03      8.277517

In [15]: df1.values[0]
Out[15]: array([2.27100000e+03, 6.98400000e+03, 3.07529723e+00])

In [16]: df1.iloc[0:2,1:4]
Out[16]: 
           price  price_per_or
beijing     6984      3.075297
hangzhou  103019      4.796713

In [17]: df1.ix[0:4,'p':'z']
/Users/natty/anaconda2/bin/ipython:1: DeprecationWarning: 
.ix is deprecated. Please use
.loc for label based indexing or
.iloc for positional indexing

See the documentation here:
http://pandas.pydata.org/pandas-docs/stable/indexing.html#ix-indexer-is-deprecated
  #!/Users/natty/anaconda2/bin/python
Out[17]: 
              price  price_per_or
beijing        6984      3.075297
hangzhou     103019      4.796713
nanjing   490291034   2431.830299
shanghai  786594932   2955.956979

In [18]: df1['beijing':'Shanghai']      #该方式对行进行索引
Out[18]: 
          order_cnt      price  price_per_or
beijing        2271       6984      3.075297
hangzhou      21477     103019      4.796713
nanjing      201614  490291034   2431.830299
shanghai     266105  786594932   2955.956979

In [19]: df1['order_cnt':'price_per_or']
Out[19]: 
Empty DataFrame
Columns: [order_cnt, price, price_per_or]
Index: []

In [20]: df1[1:3]
Out[20]: 
          order_cnt      price  price_per_or
hangzhou      21477     103019      4.796713
nanjing      201614  490291034   2431.830299

4. Pandas数值运算

4.1 索引对齐

对于一元运算(像函数与三角函数),这些通用函数将在输出结果中保留索引和列标签(很简单,所有元素做相应运算并返回);而对于二元运算(如加法和乘法),Pandas 在传递通用函数时会自动对齐索引进行计算。
当在两个 Series 或 DataFrame 对象上进行二元计算时,Pandas 会在计算过程中对齐两个对象的索引。如果想给缺失数值指定一个默认值,需要使用add来替代+,并指定fill_value:

In [21]: se_at_01 = pd.Series([7,6,9,12],index=[2,7,2,5])

In [22]: se_at_01
Out[22]: 
2     7
7     6
2     9
5    12
dtype: int64

In [23]: se_at_02 = pd.Series([13],index=[2])

In [24]: se_at_02
Out[24]: 
2    13
dtype: int64

In [25]: se_at_01 + se_at_02
Out[25]: 
2    20.0
2    22.0
5     NaN
7     NaN
dtype: float64

In [26]: se_at_01.add(se_at_02,fill_value=-99)
Out[26]: 
2    20.0
2    22.0
5   -87.0
7   -93.0
dtype: float64

4.2 dataframe对齐

两个对象的行列索引可以是不同顺序的,结果的索引会自动按顺序排列。

In [34]: df_sa_001 = pd.DataFrame(rng.randint(0,10,(2,2)),
columns=['C','D'],index=[1,2])

In [35]: df_sa_001
Out[35]: 
   C  D
1  8  4
2  7  1

In [37]: df_sa_002 = pd.DataFrame(rng.randint(0,10,(3,3)),
columns=['D','C','A'],index=[3,1,2])

In [38]: df_sa_002
Out[38]: 
   D  C  A
3  1  1  1
1  2  9  4
2  6  2  0

In [39]: df_sa_001 + df_sa_002
Out[39]: 
    A     C    D
1 NaN  17.0  6.0
2 NaN   9.0  7.0
3 NaN   NaN  NaN

4.3 dataframe与Series计算

DataFrame 和 Series 的运算规则,与NumPy 中二维数组与一维数组的运算规则是一样的。需要使用广播原则,那么默认地,会按行计算。如果想要按列运算,需要使用参数axis = 0 。

In [42]: df_sb_001 =pd.DataFrame(rng.randint(20,size=(3,4)),columns=list('QUIK'))

In [43]: df_sb_001
Out[43]: 
    Q   U   I   K
0  19   7  17   5
1  12   9  11  10
2   1  16   7   4

In [49]: se_sb_002 = pd.Series(rng.randint(20,size=(3)),index =list('QUI'))

In [52]: se_sb_002
Out[52]: 
Q    11
U    10
I     2
dtype: int64

In [50]: df_sb_001 - se_sb_002
Out[50]: 
      I   K     Q    U
0  15.0 NaN   8.0 -3.0
1   9.0 NaN   1.0 -1.0
2   5.0 NaN -10.0  6.0

In [55]: df_sb_001 - df_sb_001.iloc[0]  #按行计算
Out[55]: 
    Q  U   I  K
0   0  0   0  0
1  -7  2  -6  5
2 -18  9 -10 -1

In [60]: df_sb_001.subtract(df_sb_001['Q'],axis=0)  #按列计算
Out[60]: 
   Q   U  I   K
0  0 -12 -2 -14
1  0  -3 -1  -2
2  0  15  6   3

注意:DataFrame访问行可以使用loc,但是访问列,只能是df['col']这种形式了。

5. 缺失值

缺失值有三种形式:null、NaN 或 NA。
处理缺失值,一般有两种方法:一种方法是通过一个覆盖全局的掩码表示缺失值,另一种方法是用一个标签值(sentinel value)表示缺失值。
掩码是利用一个跟原来一样大小的矩阵,用0或者1表示某个元素缺失。标签值是利用一个特殊字符例如NaN表示缺失。
Pandas 选择用标签方法表示缺失值,包括两种 Python 原有的缺失值:浮点数据类型(包括整型)的 NaN 值,以及 Python的 None对象

NaN(全称 Not a Number,不是一个数字)

In [69]: vals_sc_001 = np.array([51,14,None,78])

In [70]: vals_sc_001
Out[70]: array([51, 14, None, 78], dtype=object)

In [71]: vals_sc_001.sum()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
 in ()
----> 1 vals_sc_001.sum()

/Users/natty/anaconda2/lib/python2.7/site-packages/numpy/core/_methods.pyc 
in _sum(a, axis, dtype, out, keepdims)
     30 
     31 def _sum(a, axis=None, dtype=None, out=None, keepdims=False):
---> 32     return umr_sum(a, axis, dtype, out, keepdims)
     33 
     34 def _prod(a, axis=None, dtype=None, out=None, keepdims=False):

TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'

In [72]: vals_sc_002 = np.array([51,14,np.nan,78])

In [73]: vals_sc_002
Out[73]: array([51., 14., nan, 78.])

In [74]: vals_sc_002.sum()
Out[74]: nan

In [75]: vals_sc_002.dtype
Out[75]: dtype('float64')

使用None时,表示一个空的python对象,所以numpy的dtype=object,因为是对象所以在进行大批量计算时,效率会比标量低。使用np.nan时表示标量,效率会高很多。

任何数与np.nan的计算都只会得到np.nan。

对于缺失值,pandas提供了几个有用的API方法,分别是:isnull(),notnull(),dropna(),fillna()。其中,对于dataframe,dropna()方法默认会将包含NaN的整行都drop掉,如果想按照整列drop,增加axis=1参数。

In [78]: df_sc_001 = pd.DataFrame(np.array([[1,43,np.nan,9],
[2,np.nan,0,1],[np.nan,np.nan,3,1]]),columns = list('fast'))

In [79]: df_sc_001
Out[79]: 
     f     a    s    t
0  1.0  43.0  NaN  9.0
1  2.0   NaN  0.0  1.0
2  NaN   NaN  3.0  1.0

In [80]: df_sc_001.isnull()
Out[80]: 
       f      a      s      t
0  False  False   True  False
1  False   True  False  False
2   True   True  False  False

In [81]: df_sc_001.notnull()
Out[81]: 
       f      a      s     t
0   True   True  False  True
1   True  False   True  True
2  False  False   True  True

In [82]: df_sc_001.fillna(-999)
Out[82]: 
       f      a      s    t
0    1.0   43.0 -999.0  9.0
1    2.0 -999.0    0.0  1.0
2 -999.0 -999.0    3.0  1.0

In [83]: df_sc_001.dropna()
Out[83]: 
Empty DataFrame
Columns: [f, a, s, t]
Index: []

In [84]: df_sc_001.dropna(axis=0)
Out[84]: 
Empty DataFrame
Columns: [f, a, s, t]
Index: []

In [85]: df_sc_001.dropna(axis=1)
Out[85]: 
     t
0  9.0
1  1.0
2  1.0

6. 层级索引

pandas的MultiIndex提供了多级索引的功能,用元组表示是多级索引的基础。

6.1. 多级索引Series

下面例子,使用元组索引生成Series。筛选2019的索引,非常繁琐。

In [87]: idx_sd_001 = [('beijing',2018),('beijing',2019),('shanghai',2018),
('shanghai',2019),('tianjin',2018),('tianjin',2019),('suzhou',2018),
('suzhou',2019)]

In [92]: se_sd_001 = pd.Series([61,62,68,69,35,39,42,45], index = idx_sd_001)

In [93]: se_sd_001
Out[93]: 
(beijing, 2018)     61
(beijing, 2019)     62
(shanghai, 2018)    68
(shanghai, 2019)    69
(tianjin, 2018)     35
(tianjin, 2019)     39
(suzhou, 2018)      42
(suzhou, 2019)      45
dtype: int64

In [95]: se_sd_001[[i for i in se_sd_001.index if i[1] == 2019]]
Out[95]: 
(beijing, 2019)     62
(shanghai, 2019)    69
(tianjin, 2019)     39
(suzhou, 2019)      45
dtype: int64

Pandas 的 MultiIndex 类型提供多种实现方法,下边例子使用元组表示实现。
unstack() 方法可以快速将一个多级索引的 Series 转化为普通索引的DataFrame。stack() 方法实现相反的效果。

In [96]: idx_sd_002 = pd.MultiIndex.from_tuples(idx_sd_001)

In [97]: idx_sd_002
Out[97]: 
MultiIndex(levels=[[u'beijing', u'shanghai', u'suzhou', u'tianjin'], [2018, 2019]],
           labels=[[0, 0, 1, 1, 3, 3, 2, 2], [0, 1, 0, 1, 0, 1, 0, 1]])

In [98]: se_sd_002 = se_sd_001.reindex(idx_sd_002)

In [99]: se_sd_002
Out[99]: 
beijing   2018    61
          2019    62
shanghai  2018    68
          2019    69
tianjin   2018    35
          2019    39
suzhou    2018    42
          2019    45
dtype: int64

In [100]: se_sd_002[:,2019]
Out[100]: 
beijing     62
shanghai    69
tianjin     39
suzhou      45
dtype: int64

In [101]: df_sd_001 = se_sd_002.unstack()

In [102]: df_sd_001
Out[102]: 
          2018  2019
beijing     61    62
shanghai    68    69
suzhou      42    45
tianjin     35    39

In [103]: se_sd_003 = df_sd_001.stack()

In [104]: se_sd_003
Out[104]: 
beijing   2018    61
          2019    62
shanghai  2018    68
          2019    69
suzhou    2018    42
          2019    45
tianjin   2018    35
          2019    39
dtype: int64

6.2. 多级索引创建方法

总结一下,创建多级索引的方法包括:
(1)通过一个有不同等级的若干简单数组组成的列表来构建 MultiIndex:pd.MultiIndex.from_arrays。
(2) 多个索引值的元组构成的列表创建 MultiIndex:pd.MultiIndex.from_tuples。
(3)用两个索引的笛卡尔积创建MultiIndex:pd.MultiIndex.from_product。
(4)直接提供 levels和labels创建 MultiIndex(lablels是指每个级别的整数指定每个位置):

In [106]: idx_sd_003 = pd.MultiIndex.from_arrays([['a','a','a','b','b'],
[0,1,2,0,1]])

In [107]: idx_sd_003
Out[107]: 
MultiIndex(levels=[[u'a', u'b'], [0, 1, 2]],
           labels=[[0, 0, 0, 1, 1], [0, 1, 2, 0, 1]])

In [109]: idx_sd_004 = pd.MultiIndex.from_product([['a','b'],[1,2]])

In [110]: idx_sd_004
Out[110]: 
MultiIndex(levels=[[u'a', u'b'], [1, 2]],
           labels=[[0, 0, 1, 1], [0, 1, 0, 1]])

In [112]: idx_sd_005 = pd.MultiIndex.from_tuples([('a',0),('a',1),('a',2),('b',0),
('b',1)])

In [113]: idx_sd_005
Out[113]: 
MultiIndex(levels=[[u'a', u'b'], [0, 1, 2]],
           labels=[[0, 0, 0, 1, 1], [0, 1, 2, 0, 1]])

In [114]: idx_sd_006 = pd.MultiIndex(levels=[['c','f','z'],[1,6,7,12]],
labels=[[0,0,1,1,2,2],[1,3,0,2,1,2]])

In [119]: idx_sd_006.values
Out[119]: 
array([('c', 6), ('c', 12), ('f', 1), ('f', 7), ('z', 6), ('z', 7)],
      dtype=object)

In [120]: idx_sd_006.names = ['rank','denk']     #设置多级索引的等级名称

In [126]: df_sd_002 = pd.DataFrame(np.random.randint(6,size=(6)),
index = idx_sd_006,columns=['order_cnt'])

In [127]: df_sd_002
Out[127]: 
           order_cnt
rank denk           
c    6             4
     12            5
f    1             1
     7             4
z    6             1
     7             3

6.3. 多级列索引

上边的例子中,不管是Series还是DataFrame都是按照行来进行多级索引,其实,也可以按列索引,而且非常简单,下边是一个例子(几个学生在2018和2019两次考试的不同科目成绩):

In [130]: idx_sd_007 = pd.MultiIndex.from_product([[2018,2019],
[1,2]],names=['year','time'])

In [134]: idx_sd_008 = pd.MultiIndex.from_product([['Alice','Judy','Bob'],
      ['Chinese','English']],names = ['name','subject'])

In [137]: df_sd_003=pd.DataFrame(np.random.randint(50,101,
size=(4,6)),index=idx_sd_007,columns=idx_sd_008)

In [138]: df_sd_003
Out[138]: 
name        Alice            Judy             Bob        
subject   Chinese English Chinese English Chinese English
year time                                                
2018 1         94      69      62      85      62      60
     2         97      97     100      88      63      84
2019 1         81      85      74      79      68      86
     2         90      81      61      91      74      77

In [139]: df_sd_003['Alice']
Out[139]: 
subject    Chinese  English
year time                  
2018 1          94       69
     2          97       97
2019 1          81       85
     2          90       81

6.4. 多级索引的取值与切片

需要对多级索引做显示切片操作时,可以使用pd.IndexSlice对象来切,不同级别的维度,拿逗号分割,例如下边例子中的df_sd_003.loc[idx_sd[2018,:],idx_sd['Alice',:]]。其他切片和取值操作与Numpy很类似。

In [140]: se_sd_003    #示例Series
Out[140]: 
beijing   2018    61
          2019    62
shanghai  2018    68
          2019    69
suzhou    2018    42
          2019    45
tianjin   2018    35
          2019    39
dtype: int64

In [141]: se_sd_003['suzhou',2019]
Out[141]: 45

In [142]: se_sd_003['suzhou']
Out[142]: 
2018    42
2019    45
dtype: int64

In [143]: se_sd_003['shanghai':'tianjin']
Out[143]: 
shanghai  2018    68
          2019    69
suzhou    2018    42
          2019    45
tianjin   2018    35
          2019    39
dtype: int64

In [144]: se_sd_003[:,2018]
Out[144]: 
beijing     61
shanghai    68
suzhou      42
tianjin     35
dtype: int64

In [145]: se_sd_003[se_sd_003>60]
Out[145]: 
beijing   2018    61
          2019    62
shanghai  2018    68
          2019    69
dtype: int64

In [146]: se_sd_003[['tianjin','suzhou']]
Out[146]: 
suzhou   2018    42
         2019    45
tianjin  2018    35
         2019    39
dtype: int64

In [147]: df_sd_003  #示例DataFrame
Out[147]: 
name        Alice            Judy             Bob        
subject   Chinese English Chinese English Chinese English
year time                                                
2018 1         94      69      62      85      62      60
     2         97      97     100      88      63      84
2019 1         81      85      74      79      68      86
     2         90      81      61      91      74      77

In [149]: df_sd_003['Judy','Chinese']
Out[149]: 
year  time
2018  1        62
      2       100
2019  1        74
      2        61
Name: (Judy, Chinese), dtype: int64

In [151]: df_sd_003.iloc[:2,:2]   #行列维度序号隐式索引
Out[151]: 
name        Alice        
subject   Chinese English
year time                
2018 1         94      69
     2         97      97

In [152]: df_sd_003.loc[:,('Alice','English')]
Out[152]: 
year  time
2018  1       69
      2       97
2019  1       85
      2       81
Name: (Alice, English), dtype: int64

In [156]: idx_sd = pd.IndexSlice

In [160]: df_sd_003.loc[idx_sd[2018,:],idx_sd['Alice',:]]
Out[160]: 
name        Alice        
subject   Chinese English
year time                
2018 1         94      69
     2         97      97

6.5. 多级索引行列转换

如果 MultiIndex 不是有序的索引,那么大多数切片操作都会失败。
如果Series或者DataFrame的索引是未排序的,可以简单地通过sort_index方法来快速排序。
层级数据维度转换的另一种方法是行列标签转换,可以通过reset_index 方法实现

In [162]: se_sf_001 = pd.Series(np.random.randint(9,size=(9)),
index=pd.MultiIndex.from_product([['c','a','b'],[4,1,3]]))

In [163]: se_sf_001
Out[163]: 
c  4    0
   1    6
   3    7
a  4    8
   1    0
   3    3
b  4    3
   1    3
   3    6
dtype: int64

In [165]: se_sf_001['a':'b']   #因为无序,切片报错

In [169]: se_sf_002 = se_sf_001.sort_index()

In [170]: se_sf_002
Out[170]: 
a  1    0
   3    3
   4    8
b  1    3
   3    6
   4    3
c  1    6
   3    7
   4    0
dtype: int64

In [171]: se_sf_002['a':'b']
Out[171]: 
a  1    0
   3    3
   4    8
b  1    3
   3    6
   4    3
dtype: int64

In [172]: se_sf_002.unstack(level=0)    #按照第一级维度unstack成dataframe
Out[172]: 
   a  b  c
1  0  3  6
3  3  6  7
4  8  3  0

In [173]: se_sf_002.unstack(level=1)    #按照第二级维度unstack成dataframe
Out[173]: 
   1  3  4
a  0  3  8
b  3  6  3
c  6  7  0

7. 合并数据集(concat和append操作)

通过pd.concat()实现pandas对象合并,pd.cancat的所有参数(下面列举的是这些参数的默认值):

pd.concat(objs, axis=0, join='outer', join_axes=None, 
ignore_index=False,keys=None, levels=None, names=None, 
verify_integrity=False,copy=True)

pd.concat() 可以简单地合并一维的 Series 或 DataFrame 对象,与
np.concatenate() 合并数组一样。
DataFrame 的合并默认都是逐行进行的(axis=0);pd.concat在合并时会保留索引,即使索引是重复的!如果设置 verify_integrity=True,那么生成重复索引时,会触发异常!有时索引无关紧要,那么合并时就可以忽略它们,可以通过设置 ignore_index 参数来实现。默认的合并方式是对所有输入列进行并集合并(join='outer'),当然也可以用 join='inner' 实现对输入列的交集合并。下面是一个实现合并的例子:

def make_df(str_d, ind):
    data = {c:[str(c) + str(i) for i in ind] for c in str_d}
    df = pd.DataFrame(data,index=ind)
    return df

ser_1 = pd.Series(list('ZERA'), index =[1,2,3,4])
ser_2 = pd.Series(list('FORD'), index =[9,10,12,14])
ser_con_1 = pd.concat([ser_1,ser_2])
ser_con_1

df_1 = make_df('AB',[1,2])
df_2 = make_df('CD',[3,4])
#DataFrame 的合并默认都是逐行进行的(axis=0)
df_con_1 = pd.concat([df_1,df_2],sort=False)

df_3 = make_df('AB', [0, 1])
df_4 = make_df('CD', [0, 1])
#axis=1修改成按列merge
df_con_2 = pd.concat([df_3,df_4],axis=1,sort=False)   #按列来合并

#verify_integrity=True会验证索引重复,所以会报错。
#df_con_3 = pd.concat([df_3,df_4],verify_integrity=True,sort=False)   
 
 #会忽略,merge的pd的索引,生成新索引。
df_con_4 = pd.concat([df_3,df_4],ignore_index=True,sort=False)      

df_con_5 = pd.concat([df_3,df_4], keys=['x','y'], sort=False)    #增加多级索引

df_5 = make_df('EFG',[2,3,5])
df_6 = make_df('FGH',[1,4,6])

# 默认: join ='outer'
df_con_6 = pd.concat([df_5,df_6],sort=False,join ='outer')  
#  join ='inner',按列找交集。
df_con_7 = pd.concat([df_5,df_6],sort=False,join ='inner')  
#另一种合并方式是直接确定结果使用的列名,设置 join_axes 参数,里面是索引对象构成的列表
df_con_8 = pd.concat([df_5,df_6],sort=False,join_axes =[df_5.columns[0:2]])

df_con_8

8. 合并数据集(合并与连接)

Pandas 的基本特性之一就是高性能的内存式数据连接(join)与合并(merge)操作。

8.1 直接数据合并

pd.merge() 实现的功能基于关系代数(relational algebra)的一部分。 pd.merge() 函数实现了三种数据连接的类型:一对一、多对一和多对多。pd.merge()会自动识别2个dataframe共有的列,并以这个列进行关联。

df1 = pd.DataFrame({'employee': ['Bob', 'Jake', 'Lisa', 'Sue'],
'group': ['Accounting', 'Engineering', 'Engineering', 'HR']})
df2 = pd.DataFrame({'employee': ['Lisa', 'Bob', 'Jake', 'Sue'],
'hire_date': [2004, 2008, 2012, 2014]})

df3 = pd.merge(df1,df2) #一对一关联

df4 = pd.DataFrame({'group': ['Accounting', 'Engineering', 'HR'],
'supervisor': ['Carly', 'Guido', 'Steve']})

df5 = pd.merge(df3,df4)  #多对一关联

df6 = pd.DataFrame({'group': ['Accounting', 'Accounting','Engineering', 'Engineering', 'HR', 'HR'],
'skills': ['math', 'spreadsheets', 'coding', 'linux','spreadsheets', 'organization']})

pd.merge(df5,df6)  #多对多关联

8.2 指定合并关联键

上边的例子中,关联的两个dataframe具有相同名称的列,pandas会直接按同名列合并,由于两个输入要合并的列通常都不是同名的,因此 pd.merge() 提供了一些参数处理这个问题。
1.最简单的方法就是直接将参数 on 设置为一个列名字符串或者一个包含多列名称的列表,这个参数只能在两个 DataFrame 有共同列名的时候才可以使用。

df1 = pd.DataFrame({'employee': ['Bob', 'Jake', 'Lisa', 'Sue'],
'group': ['Accounting', 'Engineering', 'Engineering', 'HR']})
df2 = pd.DataFrame({'employee': ['Lisa', 'Bob', 'Jake', 'Sue'],
'hire_date': [2004, 2008, 2012, 2014]})

df3 = pd.merge(df1,df2,on='employee') #一对一关联

df4 = pd.DataFrame({'group': ['Accounting', 'Engineering', 'HR'],
'supervisor': ['Carly', 'Guido', 'Steve']})

df5 = pd.merge(df3,df4,on='group')  #多对一关联

df6 = pd.DataFrame({'group': ['Accounting', 'Accounting','Engineering', 'Engineering', 'HR', 'HR'],
'skills': ['math', 'spreadsheets', 'coding', 'linux','spreadsheets', 'organization']})

df7 = pd.merge(df5,df6,on='group')  #多对多关联

df8 = pd.DataFrame({'name': ['Bob', 'Jake', 'Lisa', 'Sue'],
'salary': [70000, 80000, 120000, 90000]})

# left_on指定左DataFrame的关联键,right_on指定右DataFrame的关联键
# drop删除掉 employee或者name这2个重复的键,需要指定按列(axis=1)drop
df9 = pd.merge(df1,df8,left_on='employee',right_on= 'name').drop('name',axis=1)

df10 = df1.set_index('employee')    #修改索引
df11 = df2.set_index('employee')    #修改索引

# 通过2个dataframe的索引关联(left_index=True表名左边dataframe使用索引关联)
df12 = pd.merge(df10,df11,left_index =True, right_index =True)

#当2个dataframe通过索引关联时,可以简单的使用join()来完成关联,dataframe的join()方法
df13 = df10.join(df11)

#如果想将索引与列混合使用,那么可以通过结合 left_index 与right_on,或者结合 left_on 与 right_index 来实现
df14 = pd.merge(df2, df10, left_on='employee', right_index=True)


df15 = pd.DataFrame({'name': ['Peter', 'Paul', 'Mary'],'food': ['fish', 'beans', 'bread']},columns=['name', 'food'])
df16 = pd.DataFrame({'name': ['Mary', 'Joseph'],'drink': ['wine', 'beer']},columns=['name', 'drink'])

df17 = pd.merge(df15,df16,how='inner')
df18 = pd.merge(df15,df16,how='left')
df19 = pd.merge(df15,df16,how='right')
df20 = pd.merge(df15,df16,how='outer')

df21 = pd.DataFrame({'name': ['Bob', 'Jake', 'Lisa', 'Sue'],'rank': [1, 2, 3, 4]})
df22 = pd.DataFrame({'name': ['Bob', 'Jake', 'Lisa', 'Sue'],'rank': [3, 1, 4, 2]})

#由于输出结果中有两个重复的列名,因此 pd.merge() 函数会自动为它们增加后缀 _x 或 _y
df23 = pd.merge(df21,df22,on='name')
#如果不想要默认的_x和_y,可以通过suffixes来指定不同的后缀列名
df24 = pd.merge(df21,df22,on='name', suffixes=['_L','_R'])

df24.query("rank_L == 4 | rank_R == 3")

8. 合并数据集(合并与连接)

你可能感兴趣的:(Pandas介绍)