Pandas 数据结构小抄

上一篇: NumPy小抄

Numpy 和 Pandas 有什么不同?
如果用 python 的列表和字典来作比较, 那么可以说 Numpy 是列表形式的,而 Pandas 就是字典形式。Pandas 是基于 Numpy 构建的,让基于 Numpy 的应用变得更加简单。
要使用pandas,首先需要了解他主要两个数据结构:Series和DataFrame。

数据结构

维数名称描述
1Series带标签的一维同构数组
2DataFrame带标签的,大小可变的,二维异构表格

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 标签

SeriesNumPy 数组一样,都不用循环每个值,而且 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 对象可以自动对齐 列与索引(行标签) 的数据, 生成的结果是列和行标签的并集。

DataFrameSeries 之间执行操作时,默认操作是在 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 函数

SeriesDataFrame 可使用 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)  # ...

参考资料

1. Pandas 中文: Pandas 概览

2. Pandas API reference

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值