Python For Data Analysis-五章第二节

《Python For Data Analysis》的第五章的第二节主要对pandas模块的一些常用函数进行讲解介绍。

8.1 reindex函数

在上一章最后一节介绍了reindex函数,可以为一个DataFrame指定"新"的index而创建出一个新的dataframe数据,上一章的dataframe基本都是用字典来创建的,用字典的键key作为列索引,本节用NumPy的二维数组来创建一个dataframe再复习一下reindex函数。

import pandas as pd
import numpy as np
v = np.arange(24).reshape([6,4])
print v, "# v"
cols = "aa bb cc dd".split()
rows = "z w p x y t".split()
print cols, "# cols"
print rows, "# rows"
df0 = pd.DataFrame(v, index = rows, columns  = cols)
print df0, "# df0"
cols_new = "z w p x y m n".split()
df1 = df0.reindex(cols_new)
print df1, "# df1"

执行结果:

[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]] # v
['aa', 'bb', 'cc', 'dd'] # cols
['z', 'w', 'p', 'x', 'y', 't'] # rows
   aa  bb  cc  dd
z   0   1   2   3
w   4   5   6   7
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # df0
     aa    bb    cc    dd
z   0.0   1.0   2.0   3.0
w   4.0   5.0   6.0   7.0
p   8.0   9.0  10.0  11.0
x  12.0  13.0  14.0  15.0
y  16.0  17.0  18.0  19.0
m   NaN   NaN   NaN   NaN
n   NaN   NaN   NaN   NaN # df1

基于已有的dataframe数据df0调用reindex可以创建新的dataframe数据,对于在已有的dataframe数据df0没有的行索引在新的dataframe数据df1里行填空,当然可以使用reindex的method方法前填充ffill(用前边填充没有数据的行)或后填充bfill数据,但是如果先用method参数要求新的index必须是单调有序的否则会报错且由于pandas版本的问题差异较大,不如reindex后调用datframe的方法函数fflill和bfill还是来实现methond功能。

import pandas as pd
import numpy as np
v = np.arange(24).reshape([6,4])
cols = "aa bb cc dd".split()
rows = "z w p x y t".split()
df0 = pd.DataFrame(v, index = rows, columns  = cols)
print df0, "# df0"
cols_new = "z w p n x y m".split()
df1 = df0.reindex(index = cols_new)
print df1, "# df1 reindex"

执行结果:

   aa  bb  cc  dd
z   0   1   2   3
w   4   5   6   7
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # df0
     aa    bb    cc    dd
z   0.0   1.0   2.0   3.0
w   4.0   5.0   6.0   7.0
p   8.0   9.0  10.0  11.0
n   NaN   NaN   NaN   NaN
x  12.0  13.0  14.0  15.0
y  16.0  17.0  18.0  19.0
m   NaN   NaN   NaN   NaN # df1 reindex

新的index即cols_new里多了'm'和'n'行,这两行的各列的数据填空NaN,cols_new里少了‘t’行索引所以在新的DataFrame里没有‘t’行。

8.2 ffill方法函数

DataFrame数据的ffill函数可以前向填充空行数据,即用空行的上一行数据填空行,如果空行无前行数据则填空。

import numpy as np
v = np.arange(24).reshape([6,4])
cols = "aa bb cc dd".split()
rows = "z w p x y t".split()
df0 = pd.DataFrame(v, index = rows, columns  = cols)
print df0, "# df0"
cols_new = "r z w p n x y m".split()
df1 = df0.reindex(index = cols_new).ffill()
print df1, "# df1 ffill"

执行结果:

   aa  bb  cc  dd
z   0   1   2   3
w   4   5   6   7
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # df0
     aa    bb    cc    dd
r   NaN   NaN   NaN   NaN
z   0.0   1.0   2.0   3.0
w   4.0   5.0   6.0   7.0
p   8.0   9.0  10.0  11.0
n   8.0   9.0  10.0  11.0
x  12.0  13.0  14.0  15.0
y  16.0  17.0  18.0  19.0
m  16.0  17.0  18.0  19.0 # df1 ffill

注意cols_new里新增'r'、'm'、'n'行,‘r’位于第一行没有上一行,所以'r'行填充空NaN,而'n'行用其前行'p'的数据填充,'m'用其前行'y'行的数据来填充,新的行索引里没有't'行,所以新dataframe数据没有't'行数据。

8.3 bfill方法函数

DataFrame数据的ffill函数可以后向填充空行数据,即用空行下一行填,如果空行无下一行填空。

import pandas as pd
import numpy as np
v = np.arange(24).reshape([6,4])
cols = "aa bb cc dd".split()
rows = "z w p x y t".split()
df0 = pd.DataFrame(v, index = rows, columns  = cols)
print df0, "# df0"
cols_new = "r z w p n x y m".split()
df1 = df0.reindex(index = cols_new).bfill()
print df1, "# df1 bfill"

执行结果:

   aa  bb  cc  dd
z   0   1   2   3
w   4   5   6   7
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # df0
     aa    bb    cc    dd
r   0.0   1.0   2.0   3.0
z   0.0   1.0   2.0   3.0
w   4.0   5.0   6.0   7.0
p   8.0   9.0  10.0  11.0
n  12.0  13.0  14.0  15.0
x  12.0  13.0  14.0  15.0
y  16.0  17.0  18.0  19.0
m   NaN   NaN   NaN   NaN # df1 bfill

新值的'r'行用其下一行'z'行数据来填充,'n'用'x'行数据填充,而'm'位于最低没有下一行所以填充空NaN。

8.4 用指定数据填充行

DataFrame没有fill_value函数,但reindex可以使用fill_value参数来指定reindex后空行的数据。

import pandas as pd
import numpy as np
v = np.arange(24).reshape([6,4])
cols = "aa bb cc dd".split()
rows = "z w p x y t".split()
df0 = pd.DataFrame(v, index = rows, columns  = cols)
print df0, "# df0"
cols_new = "r z w p n x y m".split()
df1 = df0.reindex(index = cols_new,fill_value=100)
print df1, "# df1 fill_value"

执行结果:

   aa  bb  cc  dd
z   0   1   2   3
w   4   5   6   7
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # df0
    aa   bb   cc   dd
r  100  100  100  100
z    0    1    2    3
w    4    5    6    7
p    8    9   10   11
n  100  100  100  100
x   12   13   14   15
y   16   17   18   19
m  100  100  100  100 # df1 fill_value

注意新的DataFrame的'm'、'r'和'n'行都是新的行,各列都填充了100这个值。

8.5 append函数

append的时候,不是对直接对原始dataframe数据进行操作,而是在内存中建立副本,因此需要将副本指针重新赋给原始数据。append函数可以在dataframe尾部添加另外一个Dataframe,问题要考虑的是两个dataframe的列不同时会如何?行名字重复又如何?

import pandas as pd
import numpy as np
v1 = np.arange(24).reshape([6,4])
v2 = np.arange(24, 48).reshape([6,4])
cols1 = "aa bb cc dd".split()
cols2 = "aa bb cc ff".split()
rows1 = "z w p x y t".split()
rows2 = "z w p x y f".split()
df1 = pd.DataFrame(v1, index = rows1, columns  = cols1)
print df1, "# df1"
print "-" * 30
df2 = pd.DataFrame(v2, index = rows2, columns  = cols2)
print df2, "# df2"
print "-" * 30
df3 = df1.append(df2,sort=False)
print df3, "# df1 append df2"
print "-" * 30
print df3.loc['z'], "# df3.loc['z']"
print "-" * 30

执行结果:

   aa  bb  cc  dd
z   0   1   2   3
w   4   5   6   7
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # df1
------------------------------
   aa  bb  cc  ff
z  24  25  26  27
w  28  29  30  31
p  32  33  34  35
x  36  37  38  39
y  40  41  42  43
f  44  45  46  47 # df2
------------------------------
   aa  bb  cc    dd    ff
z   0   1   2   3.0   NaN
w   4   5   6   7.0   NaN
p   8   9  10  11.0   NaN
x  12  13  14  15.0   NaN
y  16  17  18  19.0   NaN
t  20  21  22  23.0   NaN
z  24  25  26   NaN  27.0
w  28  29  30   NaN  31.0
p  32  33  34   NaN  35.0
x  36  37  38   NaN  39.0
y  40  41  42   NaN  43.0
f  44  45  46   NaN  47.0 # df1 append df2
------------------------------
   aa  bb  cc   dd    ff
z   0   1   2  3.0   NaN
z  24  25  26  NaN  27.0 # df3.loc['z']
------------------------------

看来dataframe允许多行具有相同的行名字,例如'z'行在df1和df2里都有,append后df3有两行'z'。对于列如果两个dataframe的列不同,取列的交集,append后的dataframe的列数增加,两个互相没有的列上数据填空。 请注意append后的df3有多个相同的行名,通过loc获取的数据也是多行输出,例如df3.loc['z']

8.6 insert在指定位置添加一列

insert函数可以在dataframe指定位置添加一列产生新的dataframe。

import pandas as pd
import numpy as np
v1 = np.arange(24).reshape([6,4])
cols1 = "aa bb cc dd".split()
rows1 = "z w p x y t".split()
df1 = pd.DataFrame(v1, index = rows1, columns  = cols1)
print df1, "# df1"
s = pd.Series(np.arange(len(rows1)), index = rows1)
print s, "# s"
df1.insert(df1.columns.get_loc("bb"), "ee", s)
print df1, "# df1 insert"
df1.insert(df1.columns.get_loc("cc"), "ff", [9,9,9,9,9,9])
print df1, "# df1 insert"

执行结果:

   aa  bb  cc  dd
z   0   1   2   3
w   4   5   6   7
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # df1
z    0
w    1
p    2
x    3
y    4
t    5
dtype: int64 # s
   aa  ee  bb  cc  dd
z   0   0   1   2   3
w   4   1   5   6   7
p   8   2   9  10  11
x  12   3  13  14  15
y  16   4  17  18  19
t  20   5  21  22  23 # df1 insert
   aa  ee  bb  ff  cc  dd
z   0   0   1   9   2   3
w   4   1   5   9   6   7
p   8   2   9   9  10  11
x  12   3  13   9  14  15
y  16   4  17   9  18  19
t  20   5  21   9  22  23 # df1 insert

df1.columns语句是访问dataframe的列索引,dataframe无论列索引还是行索引均是dataframe的index对象有方法函数get_loc可以返回索引的整形位置信息。而dataframe的insert函数可以在某列(位置)前插入一列数据(可以是series或者列表),影响dataframe本身。

8.7 drop删除数据

dataframe的drop函数可以删除整行或整列的数据。

import pandas as pd
import numpy as np
v1 = np.arange(24).reshape([6,4])
cols1 = "aa bb cc dd".split()
rows1 = "z w p x y t".split()
df1 = pd.DataFrame(v1, index = rows1, columns  = cols1)
print df1, "# df1"
print df1.drop("p"), "# df1 drop a row"
print df1.drop(["w", "z"]), "# df1 drop rows"

执行结果:

   aa  bb  cc  dd
z   0   1   2   3
w   4   5   6   7
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # df1
   aa  bb  cc  dd
z   0   1   2   3
w   4   5   6   7
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # df1 drop a row
   aa  bb  cc  dd
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # df1 drop rows

drop函数删除指定行后产生新的数据,不影响原来的数据,如果想整列删除需要使用axis=1参数指定1轴。

import pandas as pd
import numpy as np
v1 = np.arange(24).reshape([6,4])
cols1 = "aa bb cc dd".split()
rows1 = "z w p x y t".split()
df1 = pd.DataFrame(v1, index = rows1, columns  = cols1)
print df1, "# df1"
print df1.drop("bb", axis = 1), "# df1 drop a col"
print df1.drop(["bb", "cc"], axis = 1), "# df1 drop cols"

执行结果:

   aa  bb  cc  dd
z   0   1   2   3
w   4   5   6   7
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # df1
   aa  cc  dd
z   0   2   3
w   4   6   7
p   8  10  11
x  12  14  15
y  16  18  19
t  20  22  23 # df1 drop a col
   aa  dd
z   0   3
w   4   7
p   8  11
x  12  15
y  16  19
t  20  23 # df1 drop cols

drop函数如果想印象数据本身,可以使用参数inplace=True

8.8 数据的选择

dataframe的数据的选择可以通过索引、切片实现数据的选择,按选择的顺序输出得到一个新的dataframe,也可通过布尔数组来选择数据。 这里简要的总结一下通过位置信息position或行列名字label来选择的规则:

  • df[]里放名字或名字集合列表是列选择。放切片是行选择,是数字的切片是行选择不含终点,而名字的切片行选则且含终点。
import pandas as pd
import numpy as np
v1 = np.arange(24).reshape([6,4])
cols1 = "aa bb cc dd".split()
rows1 = "z w p x y t".split()
df1 = pd.DataFrame(v1, index = rows1, columns  = cols1)
print "-" * 40
print df1, "# df1"
print "-" * 40
print df1["cc"], '# select a col'
print "-" * 40
print df1[["cc", "dd", "aa"]], '# select cols'
print "-" * 40
print df1[1:2], "# select a row"
print "-" * 40
print df1[:2], "# select rows"
print "-" * 40
print df1[2:5], "# select rows"
print "-" * 40
print df1["w":"y"], "# select rows"
print "-" * 40

执行结果:

----------------------------------------
   aa  bb  cc  dd
z   0   1   2   3
w   4   5   6   7
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # df1
----------------------------------------
z     2
w     6
p    10
x    14
y    18
t    22
Name: cc, dtype: int64 # select a col
----------------------------------------
   cc  dd  aa
z   2   3   0
w   6   7   4
p  10  11   8
x  14  15  12
y  18  19  16
t  22  23  20 # select cols
----------------------------------------
   aa  bb  cc  dd
w   4   5   6   7 # select a row
----------------------------------------
   aa  bb  cc  dd
z   0   1   2   3
w   4   5   6   7 # select rows
----------------------------------------
   aa  bb  cc  dd
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19 # select rows
----------------------------------------
   aa  bb  cc  dd
w   4   5   6   7
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19 # select rows
----------------------------------------
  • df.loc[]只有一个label型参数选择行,第二label参数列选择。一个label选一行,多个label的集合列表选多行,label的切片选多行且含终点数据。这种方式在pandas里叫select by label

1). 通过loc属性只使用一个label参数选择若干行来组建新数据。

import pandas as pd
import numpy as np
v1 = np.arange(24).reshape([6,4])
cols1 = "aa bb cc dd".split()
rows1 = "z w p x y t".split()
df1 = pd.DataFrame(v1, index = rows1, columns  = cols1)
print "-" * 40
print df1, "# df1"
print "-" * 40
print df1.loc["z"], "# select a row"
print "-" * 40
print df1.loc[["p", "w", "t"]], "# select rows"
print "-" * 40
print df1.loc["p":"t"], "# select rows"
print "-" * 40

执行结果:

----------------------------------------
   aa  bb  cc  dd
z   0   1   2   3
w   4   5   6   7
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # df1
----------------------------------------
aa    0
bb    1
cc    2
dd    3
Name: z, dtype: int64 # select a row
----------------------------------------
   aa  bb  cc  dd
p   8   9  10  11
w   4   5   6   7
t  20  21  22  23 # select rows
----------------------------------------
   aa  bb  cc  dd
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # select rows
----------------------------------------

程序的最后一条语句print df1.loc[["x", "p", "z"]]选择了df1的"x", "p", "z"三行数据。

2). loc即选择行又选择列。

import pandas as pd
import numpy as np
v1 = np.arange(24).reshape([6,4])
cols1 = "aa bb cc dd".split()
rows1 = "z w p x y t".split()
df1 = pd.DataFrame(v1, index = rows1, columns  = cols1)
print "-" * 40
print df1, "# df1"
print "-" * 40
print df1.loc["z", ["cc", "aa"]], "# select a row and cols"
print "-" * 40
print df1.loc[["p", "w", "t"], ["bb", "aa"]], "# select rows and cols"
print "-" * 40
print df1.loc["p":"t", "cc"], "# select rows and a col"
print "-" * 40
print df1.loc["p":"t", "aa":"cc"], "# select rows and a cols"
print "-" * 40

执行结果:

----------------------------------------
   aa  bb  cc  dd
z   0   1   2   3
w   4   5   6   7
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # df1
----------------------------------------
cc    2
aa    0
Name: z, dtype: int64 # select a row and cols
----------------------------------------
   bb  aa
p   9   8
w   5   4
t  21  20 # select rows and cols
----------------------------------------
p    10
x    14
y    18
t    22
Name: cc, dtype: int64 # select rows and a col
----------------------------------------
   aa  bb  cc
p   8   9  10
x  12  13  14
y  16  17  18
t  20  21  22 # select rows and cols
----------------------------------------
  • df.iloc[]的方括号里是行的位置信息position而不是label名字。有单独的一个整数、整数集合列表、整数切片、布尔数组等方式来选择行,这种方式在pandas里称作select by position
import pandas as pd
import numpy as np
v1 = np.arange(24).reshape([6,4])
cols1 = "aa bb cc dd".split()
rows1 = "z w p x y t".split()
df1 = pd.DataFrame(v1, index = rows1, columns  = cols1)
print "-" * 40
print df1, "# df1"
print "-" * 40
print df1.iloc[2], "# select a row"
print "-" * 40
print df1.iloc[2:], "# select rows"
print "-" * 40
print df1.iloc[2:, 2:], "# select rows and cols"
print "-" * 40

执行结果:

----------------------------------------
   aa  bb  cc  dd
z   0   1   2   3
w   4   5   6   7
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # df1
----------------------------------------
aa     8
bb     9
cc    10
dd    11
Name: p, dtype: int64 # select a row
----------------------------------------
   aa  bb  cc  dd
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # select rows
----------------------------------------
   cc  dd
p  10  11
x  14  15
y  18  19
t  22  23 # select rows and cols
----------------------------------------
  • 通过bool array来选择若干行数据,不能选择列。布尔数组选择可以用在loc、iloc里。
import pandas as pd
import numpy as np
v1 = np.arange(24).reshape([6,4])
cols1 = "aa bb cc dd".split()
rows1 = "z w p x y t".split()
df1 = pd.DataFrame(v1, index = rows1, columns  = cols1)
print df1, "# df1"
print df1["cc"] % 3 == 0, '# df1["cc"] % 3 == 0'
print df1[df1["cc"] % 3 == 0], 'df1[df1["cc"] % 3 == 0]'

执行结果

   aa  bb  cc  dd
z   0   1   2   3
w   4   5   6   7
p   8   9  10  11
x  12  13  14  15
y  16  17  18  19
t  20  21  22  23 # df1
z    False
w     True
p    False
x    False
y     True
t    False
Name: cc, dtype: bool # df1["cc"] % 3 == 0
   aa  bb  cc  dd
w   4   5   6   7
y  16  17  18  19 df1[df1["cc"] % 3 == 0]

df1[df1["cc"] % 3 == 0]语句会得到一个bool数组,即结果里的

z    False
w     True
p    False
x    False
y     True
t    False

借助这个布尔数组来选择df1对应为True的行,即'w'和'y'的df1的数据。

感谢Klang(金浪)智能数据看板klang.org.cn鼎力支持!