Numpy 和 Pandas 有什么不同?
如果用python
的列表和字典来作比较, 那么可以说Numpy
是列表形式的,而Pandas
就是字典形式。Pandas
是基于Numpy
构建的,让基于Numpy
的应用变得更加简单。
要使用pandas,首先需要了解他主要两个数据结构:Series和DataFrame。
Pandas 数据结构目录
数据结构
维数 | 名称 | 描述 |
---|---|---|
1 | Series | 带标签的一维同构数组 |
2 | DataFrame | 带标签的,大小可变的,二维异构表格 |
1. Series(data=None, index=None, dtype=None, name=None, copy=False, fastpath=False)
Series
是带标签的一维数组,可存储整数、浮点数、字符串、Python 对象等类型的数据。轴标签统称为索引。调用 pandas.Series
函数即可创建 Series
:
pandas.Series(data=None, index=None, dtype=None, name=None, copy=False, fastpath=False)
attr: data
上述代码中,data 支持以下数据类型:
- Python 字典
d = {'b': 1, 'a': 0, 'c': 2}
series = pd.Series(d, index=['a', 'b', 'c', 'd'])
print(series) # Pandas 用 NaN(Not a Number)表示缺失数据
# a 0.0
# b 1.0
# c 2.0
# d NaN
# dtype: float64
- 多维数组
s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
print(s)
# a -0.552301
# b -1.265240
# c 1.882226
# d 0.789184
# e 1.050798
# dtype: float64
- 标量值(如,5)
data
是标量值时,必须提供索引。Series
按索引长度重复该标量值。
s = pd.Series(5, index=['a', 'b', 'c'])
print(s)
# a 5
# b 5
# c 5
# dtype: int64
Series 类似多维数组
Series
操作与 ndarray
类似,支持大多数 NumPy
函数,还支持 slice
。
s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
print(s[0], '\n', s[:3], s[s > s.median()]) # ...
# Series.array 用于提取 Series 数组
print(s.array)
# Series 只是类似于多维数组,提取真正的多维数组,要用 Series.to_numpy()
print(s.to_numpy())
Series 类似字典
Series
类似固定大小的字典,可以用索引标签提取值或设置值。
s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
print(s['a'], s['b']) # ...
print('e' in s, 'f' in s) # True False
# get 方法可以提取 Series 里没有的标签,返回 None 或指定默认值:
print(s.get('F'), s.get('g', default='NaN'))
矢量操作与对齐 Series 标签
Series
和 NumPy
数组一样,都不用循环每个值,而且 Series
支持大多数 NumPy
多维数组的方法。
s = pd.Series(np.random.randint(10, size=5), index=['a', 'b', 'c', 'd', 'e'])
print(s+s)
print(np.square(s))
Series 和多维数组的主要区别在于, Series
之间的操作会自动基于标签对齐数据。因此,不用顾及执行计算操作的 Series
是否有相同的标签。
print(s[1:] * s[:-1])
# a NaN
# b 1.0
# c 9.0
# d 16.0
# e NaN
# dtype: float64
如上所示,操作未对齐索引的 Series
, 其计算结果是所有涉及索引的并集。如果在 Series
里找不到标签,运算结果标记为 NaN
, 即缺失值。 编写无需显式对齐数据的代码,给交互数据分析和研究提供了巨大的自由度和灵活性。
2. DataFrame
DataFrame
是由多种类型的列构成的二维标签数据结构,类似于 Excel 、SQL 表,或 Series 对象构成的字典。DataFrame
是最常用的 Pandas
对象,调用 pandas.DataFrame
函数即可创建 DataFrame
:
pandas.DataFrame(data=None, index=None, columns=None, dtype=None, copy=False)
attr: data
与 Series
一样,DataFrame
支持多种类型的输入数据:
- 一维 ndarray、列表、字典、Series 字典
# 一维 ndarray
array1 = np.arange(2, 5)
print(pd.DataFrame(array1, columns=['num'], index=['a', 'b', 'c']))
"""
num
a 2
b 3
c 4
"""
# 列表
print(pd.DataFrame([2, 3, 4], columns=['num'], index=['a', 'b', 'c'])) # 输出同上
# 字典
d = {'b': 1, 'a': 0, 'c': 2}
df = pd.DataFrame(d, index=['t'])
print(df)
"""
b a c
t 1 0 2
"""
# Series 字典
s1 = pd.Series([0, 1, 2], index=['a', 'b', 'c'])
s2 = pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])
d_series = {"one": s1, "two": s2}
print(pd.DataFrame(d_series))
"""
one two
a 0.0 1.0
b 1.0 2.0
c 2.0 3.0
d NaN 4.0
"""
- 二维 numpy.ndarray
# 二维 numpy.ndarray
array_2 = np.arange(2, 14).reshape((2, 6))
print(pd.DataFrame(array_2, index=[chr(i) for i in range(97, 97 + 2)], columns=['num' + str(i) for i in range(6)]))
- 结构多维数组或记录多维数组
# 用结构多维数组或记录多维数组生成 DataFrame
data = np.zeros((2,), dtype=[('A', 'i4'), ('B', 'f4'), ('C', 'a10')])
# print(data, data.shape)
# [(0, 0., b'') (0, 0., b'')] (2,)
data[:] = [(1, 2., 'Hello'), (2, 3., "World")]
print(pd.DataFrame(data))
"""
A B C
0 1 2.0 b'Hello'
1 2 3.0 b'World'
"""
print(pd.DataFrame(data, index=['first', 'second'], columns=['C', 'A', 'B']))
"""
C A B
first b'Hello' 1 2.0
second b'World' 2 3.0
"""
- Series
生成的 DataFrame
继承了输入的 Series
的索引
s = pd.Series([0, 1, 2], index=['a', 'b', 'c'])
print(pd.DataFrame(s, columns=['num']))
"""
num
a 0
b 1
c 2
"""
- DataFrame
# DataFrame
s1 = pd.Series([0, 1, 2], index=['a', 'b', 'c'])
s2 = pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])
d_series = {"one": s1, "two": s2}
df = pd.DataFrame(d_series)
d = pd.DataFrame(df, index=['a', 'c', 'd'], columns=['two', 'three'])
print(d)
"""
two three
a 1.0 NaN
c 3.0 NaN
d 4.0 NaN
"""
备选构建器
DataFrame.from_dict
DataFrame.from_dict
可以接收字典,并生成 DataFrame
。并且 orient
参数默认为 columns
,表示字典的键将作为列标签。当把 orient
参数设置为 index
时即可 把字典的键作为行标签。
d = [('A', [1, 2, 3]), ('B', [4, 5, 6])]
print(pd.DataFrame.from_dict(dict(d)))
"""
A B
0 1 4
1 2 5
2 3 6
"""
print(pd.DataFrame.from_dict(dict(d), orient='index', columns=['one', 'two', 'three']))
"""
one two three
A 1 2 3
B 4 5 6
"""
DataFrame.from_records
DataFrame.from_records
构建器支持元组列表或结构数据类型(dtype)的多维数组。本构建器与 DataFrame 构建器
类似,只不过生成的 DataFrame 索引是结构数据类型指定的字段。
data = np.zeros((2,), dtype=[('A', 'i4'), ('B', 'f4'), ('C', 'a10')])
data[:] = [(1, 2., 'Hello'), (2, 3., "World")]
print(pd.DataFrame.from_records(data, index='C'))
"""
A B
C
b'Hello' 1 2.0
b'World' 2 3.0
"""
提取、添加、删除列
DataFrame
就像带索引的 Series
字典,提取、设置、删除列的操作与字典类似:
d_series = {"one": pd.Series([0, 1, 2], index=['a', 'b', 'c']), "two": pd.Series([1., 2., 3., 4.], \
index=['a', 'b', 'c', 'd'])}
df = pd.DataFrame(d_series)
# 提取
print(df['one']) # ...
# 设置
df['three'] = df['one'] * df['two']
df['four'] = df['one'] > 2
print(df)
"""
one two three four
a 0.0 1.0 0.0 False
b 1.0 2.0 2.0 False
c 2.0 3.0 6.0 False
d NaN 4.0 NaN False
"""
# 删除
del df['four']
df_three = df.pop('three')
print(df)
"""
one two
a 0.0 1.0
b 1.0 2.0
c 2.0 3.0
d NaN 4.0
"""
此外标量值以广播的方式填充列:
df['foo'] = 'bar'
print(df)
"""
one two foo
a 0.0 1.0 bar
b 1.0 2.0 bar
c 2.0 3.0 bar
d NaN 4.0 bar
"""
可以插入原生多维数组,但长度必须与 DataFrame
索引长度一致。
默认在 DataFrame
尾部插入列。insert 函数
可以指定插入列的位置:
arr = [1, 2, 3, 4]
df.insert(0, 'bar', arr) # def insert(loc: int, column, value ...
print(df)
"""
bar one two three four
a 1 0.0 1.0 0.0 False
b 2 1.0 2.0 2.0 False
c 3 2.0 3.0 6.0 False
d 4 NaN 4.0 NaN False
"""
用方法链分配新列
DataFrame
提供了 assign()
方法,可以利用现有的列创建新列。assign
返回的都是数据副本,原 DataFrame
不变。
素材 csv 文件来源:The Iris Dataset
iris = pd.read_csv('iris.csv')
print(iris.head(2))
"""
sepal_length sepal_width petal_length petal_width species
0 5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
"""
print(iris.assign(sepal_ratio=iris['sepal_width'] / iris['sepal_length']).head(2))
print(iris.assign(sepal_ratio=lambda x: x['sepal_width'] / iris['sepal_length']).head(2)) # 输出应相同
"""
sepal_length sepal_width petal_length petal_width species sepal_ratio
0 5.1 3.5 1.4 0.2 setosa 0.686275
1 4.9 3.0 1.4 0.2 setosa 0.612245
"""
未引用 DataFrame
时,传递可调用的(可以是lambda
),不是实际要插入的值。这种方式常见于在操作链中调用 assign
的操作。例如,将 DataFrame
限制为花萼长度大于 5 的观察值,计算比例,再制图:
iris = pd.read_csv('iris.csv')
iris.query('sepal_length > 5').assign(sepal_ratio=lambda x: x['sepal_width'] / iris['sepal_length'],
petal_ratio=lambda x: x['petal_width'] / x['petal_length']).plot(kind='scatter',
x='sepal_ratio',
y='petal_ratio')
plt.show()
索引 / 选择
索引基础用法如下:
操作 | 句法 | 结果 |
---|---|---|
选择列 | df[col] | Series |
用标签选择行 | df.loc[label] | Series |
用整数位置选择行 | df.iloc[loc] | Series |
行切片 | df[5:10] | DataFrame |
用布尔向量选择行 | df[bool_vec] | DataFrame |
以上 loc、iloc
等都是基于 index
做的相应的操作,而布尔序列值 (bool_vec)
来选择 非index列
的值作用范围比 loc
等要宽泛一些、用途较为广泛,即给出一个布尔的列表来选择对应的行,对于逻辑判断是作用于每一行上面的。
val = np.arange(10, 50).reshape(5, 8)
col = [chr(i) + 'x' for i in range(97, 97 + 8)]
idx = [chr(i) for i in range(97, 97 + 5)]
df1 = pd.DataFrame(val, columns=col, index=idx)
print("*" * 11, "dataframe", "*" * 11)
print(df1)
print("*" * 10, "df[\"bs\"]=...", "*" * 10)
bs = (df1["bx"] > 30) & (df1['cx'] > 40)
print(df1[bs])
"""
*********** dataframe ***********
ax bx cx dx ex fx gx hx
a 10 11 12 13 14 15 16 17
b 18 19 20 21 22 23 24 25
c 26 27 28 29 30 31 32 33
d 34 35 36 37 38 39 40 41
e 42 43 44 45 46 47 48 49
********** df["bs"]=... **********
ax bx cx dx ex fx gx hx
e 42 43 44 45 46 47 48 49
"""
数据对齐和运算
DataFrame
对象可以自动对齐 列与索引(行标签) 的数据, 生成的结果是列和行标签的并集。
DataFrame
和 Series
之间执行操作时,默认操作是在 DataFrame
的列上对齐 Series
的索引,按行执行广播操作。例如:
idx = [chr(i) for i in range(97, 97 + 5)]
df = pd.DataFrame(np.random.randint(1, 11, 20).reshape((5, 4)), columns=['A', 'B', 'C', 'D'], index=idx)
print(df - df.iloc[0])
# A B C D
# a 0 0 0 0
# b 2 1 2 -5
# c 9 0 7 3
# d 4 5 1 4
# e 9 0 3 1
此外, DataFrame
对象支持布尔运算符与取反, 即下面的运算是合法的:
df1 = pd.DataFrame({'a': [1, 0, 1], 'b': [0, 1, 1]}, dtype=bool)
df2 = pd.DataFrame({'a': [0, 1, 1], 'b': [1, 1, 0]}, dtype=bool)
print(df1 & df2)
print(df1 | df2)
print(df1 ^ df2)
print(-df2)
转置
类似于多维数组,T 属性(即 transpose
函数)可以转置 DataFrame
:
df = pd.DataFrame({'col1': np.random.randint(1, 10, 3), 'col2': np.random.randint(1, 10, 3)}, index=['a', 'b', 'c'])
print(df)
# col1 col2
# a 9 7
# b 7 6
# c 5 1
print(df.T) # == print(df.transpose())
# a b c
# col1 9 7 5
# col2 7 6 1
DataFrame 应用 NumPy 函数
Series
与 DataFrame
可使用 log、exp、sqrt
等多种元素级 NumPy 通用函数(ufunc)
,假设 DataFrame
的数据都是数字, 则下面的操作都是合法的:
idx = [chr(i) for i in range(97, 97 + 4)]
cols = [chr(i) for i in range(65, 65 + 4)]
df = pd.DataFrame(np.random.randint(1, 10, 16).reshape((4, 4)), index=idx, columns=cols)
print(np.exp(df)) # ...
np.asarray(df) # ...