pandas 是 Python 下科学计算非常实用的一个工具。本文将简明扼要地介绍其使用方法。如果想要初步地操作数据,直接阅读本文即可;否则请先学习 numpy 相关的内容。
引言:Series 与 DataFrame
加载 pandas 时,一般也会同时加载 numpy. 默认使用如下的语句:
import numpy as np
import pandas as pd
from pandas import Series, DataFrame
pandas 内置了两种独特的数据结构:Series 与 DataFrame.
系列(Series)
系列是由多个同类型元素组成的有序列向量,有些接近 Python 原生的列表(list)。不同的是,系列包含各元素的一个索引:这个索引可以任意重命名。
s = Series([1, 2, np.nan, 4])
s
0 1.0
1 2.0
2 NaN
3 4.0
dtype: float64
s.index = list('abcd')
s
a 1.0
b 2.0
c NaN
d 4.0
dtype: float64
s["c"]
nan
数据框(DataFrame)
数据框是一个二维的数据结构,特点是:
列内的元素同类型,不同的列之间可以不相同。
索引有两个轴向:axis=0/”index”行,axis=1/”columns” 列。分别用 df.index(行名)与df.columns(列名)调用。
在数据处理中,使用数据框是非常便捷的;而系列我们却很少使用。
数据读写
数据读取
pandas 内置的文件读取函数:
pd.read_csv():参数如下。
names:指定表头(df.columns)。
header:指定整数 n,表示前 n 行会被读作表头。如果 names 指定了,会被设为 0.
engine: “c” 或者 “python”。前者会快一些。
skipinitialspace: 是否忽略紧跟在分隔符后的空格。默认值 False.
na_values:指定一个字符串列表,里面的字符串都会被识别为 NaN。默认的有 [‘’, ‘#N/A’, ‘#N/A N/A’, ‘#NA’, ‘-1.#IND’, ‘-1.#QNAN’, ‘-NaN’, ‘-nan’,‘1.#IND’, ‘1.#QNAN’, ‘N/A’, ‘NA’, ‘NULL’, ‘NaN’, ‘nan’.]
na_filter:默认值 True。如果确定文件中不含 NaN,那么指定它为 False 可以提高大文件读取速度。 -skip_blank_lines:是否跳过空白行。默认值 True.
pd.read_table():只要是分隔符文件即可。参数 sep 缺省值是制表符。
pd.read_excel()
以上读取的结果都是 DataFrame. 其他的读取函数不再介绍。
数据写入
df.to_csv()
df.to_excel()
数据创建
以下介绍几种数据框的创建方法。第一种,如果是全数字的数据结构,可以直接从 numpy 中的矩阵创建。
# 参数 index 与 columns 是可选的
arr = np.arange(1, 19).reshape([6, 3])
df = DataFrame(arr, index=list('ABCDEF'), columns=list('ZYX'))
df
ZYX
A
1
2
3
B
4
5
6
C
7
8
9
D
10
11
12
E
13
14
15
F
16
17
18
第二种,通过字典创建。字典的每个键下的值都是格式相同的列表。注意:此时的列会自动根据列名(原字典中的键)从左到右升序排列。
df = DataFrame({"Letters": ["A", "B"], "Age": [12, 34]})
df
AgeLetters
0
12
A
1
34
B
数据框尺寸:df.shape 与 len()
与 numpy 的矩阵类似,数据框拥有属性 shape,返回结果是一个元组:
df = DataFrame(arr, index=list('ABCDEF'), columns=list('ZYX'))
df.shape
(6, 3)
如果使用 Python 原生的 len() 函数,会返回数据框的行数。注意:如果你要基于数据框写循环语句,请利用它们的行名(df.index)而不是利用 len() 函数。这是因为数据框在切片时会保留行名索引;如果简单地使用 for row in range(len(...)) ,会出现 row 与行名不一致的问题。
len(df)
6
预览数据:df.head() / tail() / values
常用的是前两个,用于查看数据框头部 5 行或者尾部 5 行的数据。你也可以传入数字指定查看的行数。
df.head(3)
ZYX
A
1
2
3
B
4
5
6
C
7
8
9
df.tail()
ZYX
B
4
5
6
C
7
8
9
D
10
11
12
E
13
14
15
F
16
17
18
df.values 则是一个有些怪异的命令:它会返回一个 np.array 类型的数据。
df.values
array([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12],
[13, 14, 15],
[16, 17, 18]])
查看 df 的基础信息,使用 df.info() 命令:
df.info()
Index: 6 entries, A to F
Data columns (total 3 columns):
Z 6 non-null int32
Y 6 non-null int32
X 6 non-null int32
dtypes: int32(3)
memory usage: 96.0+ bytes
统计信息:df.describe(), s.value_counts() / unique()
df.describe()
ZYX
count
6.000000
6.000000
6.000000
mean
8.500000
9.500000
10.500000
std
5.612486
5.612486
5.612486
min
1.000000
2.000000
3.000000
25%
4.750000
5.750000
6.750000
50%
8.500000
9.500000
10.500000
75%
12.250000
13.250000
14.250000
max
16.000000
17.000000
18.000000
value_counts() 是一个适用于 Series 的命令。参数 dropna 可以使用。
df.apply(Series.value_counts, dropna=False)
ZYX
1
1.0
NaN
NaN
2
NaN
1.0
NaN
3
NaN
NaN
1.0
4
1.0
NaN
NaN
5
NaN
1.0
NaN
6
NaN
NaN
1.0
7
1.0
NaN
NaN
8
NaN
1.0
NaN
9
NaN
NaN
1.0
10
1.0
NaN
NaN
11
NaN
1.0
NaN
12
NaN
NaN
1.0
13
1.0
NaN
NaN
14
NaN
1.0
NaN
15
NaN
NaN
1.0
16
1.0
NaN
NaN
17
NaN
1.0
NaN
18
NaN
NaN
1.0
unique() 命令有时会配合集合(set)数据结构一同使用:
tmp = Series([1, 2, 3, np.nan, 14, 3, 1])
set_1 = set(tmp.unique())
set_1
{nan,1.0,2.0,3.0,14.0}
更改列类型:pd.to_numeric()
pandas 在读入数据时会自动识别各列的类型。识别的类型可以使用 dtypes 属性:
df.dtypes
Z int32
Y int32
X int32
dtype: object
例如,我们将前两列的属性改为字符串:(这里使用的 applymap 函数会在后面提到)
df[["Z", "Y"]] = df[["Z", "Y"]].applymap(str)
df.dtypes
Z object
Y object
X int32
dtype: object
# Check it
df["Z"][1]
'4'
对于上述的问题,astype() 命令也是一个解决方案:
df[["Z", "Y"]] = df[["Z", "Y"]].astype(str)
如果想转换为数字,pandas 提供了 to_numeric 命令。此命令是作用于 Series 的,因此你可能需要 apply 命令来应用于多个列:
df[["Z", "Y"]] = df[["Z", "Y"]].apply(pd.to_numeric)
df.dtypes
Z int64
Y int64
X int32
dtype: object
注意到该命令有个 errors 参数,用于处理无法正常转换的情况。可以指定以下值:
“ignore”: 如果列中存在无法转换的元素,那么整个列不作转换;
“coerce”:如果列中存在无法转换的元素,仍然转换列,并将这些元素转换为 NaN
tmp = Series([1, 2, "Tree", 4])
pd.to_numeric(tmp, errors="ignore")
0 1
1 2
2 Tree
3 4
dtype: object
pd.to_numeric(tmp, errors="coerce")
0 1.0
1 2.0
2 NaN
3 4.0
dtype: float64
更改行 / 列名:df.rename()
数据框的行名 / 列名列表分别用 df.columns 与 df.index 查看。
df.columns
Index(['Z', 'Y', 'X'], dtype='object')
df.index
Index(['A', 'B', 'C', 'D', 'E', 'F'], dtype='object')
因此,最简单粗暴的行名 / 列名更改方法是:将一个列表赋值过去。
df.columns = list("RQP")
df.head(1)
RQP
A
1
2
3
有时需要只更改几个名称,难道需要创建整个列表吗?这时候使用 df.rename 命令,通过字典的方式替换:
# 在很多函数中,inplace 参数都避免了自赋值的操作
df.rename(columns = {"R": "Z", "Q": "Y", "P": "X"}, inplace=True)
df.head(1)
ZYX
A
1
2
3
数据整理
深度复制:df.copy()
不用多说。如果不进行深度复制,df 与 df2 将会是同址的。
df2 = df.copy()
关于切片时的深度复制问题,会在介绍切片时阐述。
转置:df.T
df.T # 不改变 df
ABCDEF
Z
1
4
7
10
13
16
Y
2
5
8
11
14
17
X
3
6
9
12
15
18
排序
排序分为两种:
按照列名/行名的字母顺序排序列/行,这里称为索引重排;
按照列内/行内的数据间大小关系,排序整个数据框的行/列,这里称为(数据)排序。
索引重排:df.sort_index()
默认参数是 axis=0(重排行),ascending=True(升序)。支持 inplace 参数。
df.sort_index(ascending=False)
ZYX
F
16
17
18
E
13
14
15
D
10
11
12
C
7
8
9
B
4
5
6
A
1
2
3
df.sort_index(axis="columns") # 或 axis = 1
XYZ
A
3
2
1
B
6
5
4
C
9
8
7
D
12
11
10
E
15
14
13
F
18
17
16
数据排序:df.sort_values()
此时只能根据列来排序。参数 na_position 默认值是 “last”,也可以指定 “first”.
df.sort_values(by="Z", ascending=False)
ZYX
F
16
17
18
E
13
14
15
D
10
11
12
C
7
8
9
B
4
5
6
A
1
2
3
切片与选取
普通选取
选取单列:df['X'] 或者 df.X
选取多列:df[['X', 'Y']]
选取行:例如前两行 df[:2]
按照标签:df.loc['A'] / df.loc[['A', 'E'], 'X']
按照索引序号:第三行 df.iloc[2];df.iloc[:2, :1]
单元素选取:df.at / df.iat,只能选取单个元素。
在多数情况下,请尽量使用 loc/iloc 命令。它们是深度复制命令,这样可以避免多数的引用问题。
布尔型选取
df[(df.X > 10) & (df.Y < 15)]
ZYX
D
10
11
12
E
13
14
15
df[df > 10] # 对整个 df 进行判断,不符合的填充 NaN
ZYX
A
NaN
NaN
NaN
B
NaN
NaN
NaN
C
NaN
NaN
NaN
D
NaN
11.0
12.0
E
13.0
14.0
15.0
F
16.0
17.0
18.0
df[df.X.isin([3, 6, 8])] # 根据列 X 中是否有列表里对应元素,筛选行
ZYX
A
1
2
3
B
4
5
6
缺失数据:df.dropna() / fillna()
df.dropna() 将筛选含有 NaN 的行/列(用参数 axis 指定),支持 inplace 参数。参数 how 指定筛选的方式:
df.dropna(how=”any”):行/列中存在 NaN 就抛弃。
df.dropna(how=”all”):只有当行/列全为 NaN 时才抛弃。
参数 thresh 指定了行/列中最低应存在的非 NaN 元素个数;如果非 NaN 元素个数小于这一阈值,该行/列会被抛弃。
df.fillna() 将 NaN 替换为另外的数值,用参数 value 指定。例如:df.fillna(value=-1)。
pd.isnull() 命令将会返回一个布尔型数据框,检验各元素是否为 NaN。
pd.isnull(df).head(2)
ZYX
A
False
False
False
B
False
False
False
查找替换:df.replace()
df.replace([1, 2], [100, 200])
ZYX
A
100
200
3
B
4
5
6
C
7
8
9
D
10
11
12
E
13
14
15
F
16
17
18
合并与分组
合并:df.append() / join(), pd.concat()
append() 是源自 list 类型的函数,类似 list 类型的 DataFrame 也可以调用。其作用是行合并。
df1 = df
df2 = df.rename(columns={"X": "Z", "Z": "P"})
df1.append(df2)
PXYZ
A
NaN
3.0
2
1
B
NaN
6.0
5
4
C
NaN
9.0
8
7
D
NaN
12.0
11
10
E
NaN
15.0
14
13
F
NaN
18.0
17
16
A
1.0
NaN
2
3
B
4.0
NaN
5
6
C
7.0
NaN
8
9
D
10.0
NaN
11
12
E
13.0
NaN
14
15
F
16.0
NaN
17
18
pd.concat() 则是一个可以指定合并轴的函数:
pd.concat([df1, df2], axis="columns") # 列合并
ZYXPYZ
A
1
2
3
1
2
3
B
4
5
6
4
5
6
C
7
8
9
7
8
9
D
10
11
12
10
11
12
E
13
14
15
13
14
15
F
16
17
18
16
17
18
如果按默认的合并方式 join=”outer”,那么 concat(axis="index") 的结果与 append() 一致。但你可以指定内联合并:
pd.concat([df1, df2], axis="index", join="inner")
YZ
A
2
1
B
5
4
C
8
7
D
11
10
E
14
13
F
17
16
A
2
3
B
5
6
C
8
9
D
11
12
E
14
15
F
17
18
df.join() 则允许你使用类似 SQL 的方法进行合并。利用其参数 how 与 on 等可以控制合并方式。
分组:df.groupby()
df.groupby() 函数与 R 语言中的 aggregate() 有异曲同工之妙。
dt = DataFrame({"A": [1, 2, 3, 4, 5, 6],
"B": ["a", "a", "a", "b", "b", "b"],
"C": ["x", "x", "y", "y", "z", "z"],
"D": [1, 1, 1, 2, 2, 2]})
dt.groupby("B")
返回值无法直接理解,需要配合统计函数使用。可以参考本文“统计函数”一节的内容。
dt.groupby("B").mean() # 列 C 无法计算 mean(),被忽略
AD
B
a
2
1
b
5
2
也可以同时应用到多个列。如果你了解过 R 语言中“因子”的概念,会对该函数的理解有所帮助。列 B 有两个水平,列 C 有三个。
dt.groupby(["B", "C"]).mean()
AD
BC
ax
1.5
1.0
y
3.0
1.0
by
4.0
2.0
z
5.5
2.0
groupby() 函数默认会将函数应用到所有其他的列。如果你只想计算指定的列,那么:
dt.groupby(["B", "C"])["A"].mean()
B C
a x 1.5
y 3.0
b y 4.0
z 5.5
Name: A, dtype: float64
函数式应用:df.apply()/applymap()
df.apply() 函数在前面已经介绍过。参数主要是 axis,以及也能继承要应用的函数的参数。所有可以应用于 Series 的函数都能够这样应用到不同的列上。这里再次使用上文使用过的 Series.value_counts() 函数。
df.apply(Series.value_counts, dropna=False).head(4)
ZYX
1
1.0
NaN
NaN
2
NaN
1.0
NaN
3
NaN
NaN
1.0
4
1.0
NaN
NaN
一个用 df.applymap() 实现的小数位数处理,保留到第二位:
df.applymap(lambda x: "%.2f" % x)
ZYX
A
1.00
2.00
3.00
B
4.00
5.00
6.00
C
7.00
8.00
9.00
D
10.00
11.00
12.00
E
13.00
14.00
15.00
F
16.00
17.00
18.00
统计函数
除了上文介绍过的 df.describe(),以下运算均以“列”为基本单位:
df.mean()
df.corr()
df.count():非 NaN 数据计数
df.max()/df.minx()
df.median()
df.std()
其他
如果你有更多的疑问,请参阅 numpy 一文。
原文链接:https://wklchris.github.io/Py3-pandas.html