在线调试运行
目标
了解 Pandas 的数据结构并实现常用的表格操作。
在学习本教程后,你将能够:
- 了解 Pandas 的数据结构对象
- 使用 Pandas 创建、读取或导出数据
- 查看或选择数据,对数据进行处理(选择,删除,分组,分类等)
- 表格处理,例如多表合并,改变表的数据结构
- 使用 Pandas 时间序列数据
- 结合 matplotlib 对 Pandas 数据进行绘图
阅读该教程【最多】约需 15 分钟,让我们开始吧!
目录
背景
这是对 Pandas 的简短介绍,主要面向新用户。您可以在这里查看更进一步的教程。
你需要提前掌握以下知识:
- 非常基本的 Python 知识
实践
1 认识 Pandas
在这一部分,你会了解什么是 Pandas,在 Bohrium 中使用 Pandas,验证安装并查看版本。
1.1 什么是 Pandas
Pandas 是一个用于处理数据集的 Python 库。它具有分析、清理、探索和操作数据的功能。 “Pandas”这个名称参考了“Panel Data”和“Python Data Analysis”,由 Wes McKinney 于 2008 年创建。Pandas 广泛用于数据科学、数据分析和机器学习任务。
官方强调了其如下显著特点[4]:
-
轻松处理浮点数和非浮点数中的 缺失数据(表示为
NaN
、NA
或NaT
) -
大小可变性:可以从 DataFrame 和更高维度的对象中插入和删除 列。
-
自动和显式数据对齐,对象可以显式地对齐到一组标签,或者用户可以简单地忽略标签,让 Series、DataFrame 等在计算中自动对齐数据。
-
提供了强大、灵活的分组功能,可以对数据集执行分割-应用-组合操作,用于聚合和转换数据。
-
以将 Python 和 NumPy 数据结构中的不同索引、不规则数据轻松转换为 DataFrame 对象。
-
轴的分层标签(每个刻度可以有多个标签)
-
强大的 IO 工具,可从平面文件(CSV 和分隔符)、Excel 文件、数据库加载数据,并从超快的 HDF5 格式保存/加载数据
-
时间序列特定功能:日期范围生成和频率转换、移动窗口统计、日期移位和滞后
1.2 安装 Pandas
本教程是一个 Bohrium Notebook。Python 程序可直接在浏览器中运行,Bohrium 已安装 Pandas。
要按照本教程进行操作,请点击本页顶部的按钮,在 Bohrium Notebook 中运行本笔记本。
-
你可以点击界面上方蓝色按钮
开始连接
,选择bohrium-notebook:2023-02-28
镜像及任何一款计算机型,稍等片刻即可运行。 -
若要运行笔记本中的所有代码,请点击左上角“ 运行全部单元格 ”。若要一次运行一个代码单元,请选择需要运行的单元格,然后点击左上角 “运行选中的单元格” 图标。
如果你的 Bohrium 镜像尚未安装 Pandas, 最方便的方法是通过 pip 安装:
! pip install pandas
Requirement already satisfied: pandas in /opt/anaconda3/envs/ML_env/lib/python3.9/site-packages (1.5.1)
Requirement already satisfied: python-dateutil>=2.8.1 in /Users/harvey/.local/lib/python3.9/site-packages (from pandas) (2.8.2)
Requirement already satisfied: pytz>=2020.1 in /opt/anaconda3/envs/ML_env/lib/python3.9/site-packages (from pandas) (2022.1)
Requirement already satisfied: numpy>=1.20.3 in /opt/anaconda3/envs/ML_env/lib/python3.9/site-packages (from pandas) (1.23.4)
Requirement already satisfied: six>=1.5 in /opt/anaconda3/envs/ML_env/lib/python3.9/site-packages (from python-dateutil>=2.8.1->pandas) (1.16.0)
如果你需要使用更特定于你的平台或包管理器的安装方法,你可以在这里查看更完整的安装说明。
1.3 验证 Pandas 安装并查看版本
安装 Pandas 后,确认库已成功安装并且你可以开始使用它。
不要跳过此步骤。
如果 Pandas 未正确安装或在此步骤中引发错误,则将无法运行之后的示例。
import pandas as pd
print(pd.__version__) # pd.__version__ 返回安装的 Pandas 的版本号
1.5.1
2 Pandas 方法
在这一节中,你会了解到一些 Pandas 的基础方法,包括:
- 2.1 创建对象
- 2.2 查看数据
- 2.3 选择数据
- 2.4 删除缺失值
- 2.5 操作数据
- 2.6 合并表格
- 2.7 数据分组
- 2.8 改变数据结构
- 2.9 时间序列
- 2.10 分类数据
- 2.11 绘图
- 2.12 读取与导出数据
2.1 创建对象
Pandas 的数据对象分为两类,一类为 Series
,一类为 DataFrame
。
Series
是一个一维标记数组,能够保存任何数据类型(整数、字符串、浮点数、Python 对象等)。轴标签统称为索引。
DataFrame
是一种二维标记的数据结构,具有可能不同类型的列。您可以将其视为电子表格或SQL表,或系列对象的字典。它通常是最常用的 Pandas 对象。
查看更多 Pandas 数据结构的信息,请阅读 Intro to data structures。
2.1.1 创建一个 Series
通过传递一个值列表,让 Pandas 创建一个默认的整数索引。
import pandas as pd
import numpy as np
s = pd.Series([1, 3, 5, np.nan, 6, 8])
s
0 1.0
1 3.0
2 5.0
3 NaN
4 6.0
5 8.0
dtype: float64
2.1.2 创建一个 DataFrame (方法 1)
通过传递一个 NumPy 数组,使用 date_range() 创建一个日期时间索引,并使用标记列进行标记。
dates = pd.date_range("20130101", periods=6)
dates
DatetimeIndex(['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04',
'2013-01-05', '2013-01-06'],
dtype='datetime64[ns]', freq='D')
df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list("ABCD"))
df
A | B | C | D | |
---|---|---|---|---|
2013-01-01 | 0.732649 | 0.452911 | 0.050417 | 0.234816 |
2013-01-02 | -1.127360 | 1.024067 | 2.355411 | 0.834325 |
2013-01-03 | -0.346012 | -1.059302 | -0.927076 | -0.518720 |
2013-01-04 | 0.793778 | -0.880850 | -0.140322 | 1.771748 |
2013-01-05 | -0.465872 | 0.074124 | -0.884347 | -0.125452 |
2013-01-06 | 0.865522 | -0.773847 | -1.754687 | -1.171656 |
2.1.3 创建一个 DataFrame (方法 2)
通过传递一个可以转换为类似于 Series 的结构的对象字典。
df2 = pd.DataFrame(
{
"A": 1.0,
"B": pd.Timestamp("20130102"),
"C": pd.Series(1, index=list(range(4)), dtype="float32"),
"D": np.array([3] * 4, dtype="int32"),
"E": pd.Categorical(["test", "train", "test", "train"]),
"F": "foo",
}
)
df2
A | B | C | D | E | F | |
---|---|---|---|---|---|---|
0 | 1.0 | 2013-01-02 | 1.0 | 3 | test | foo |
1 | 1.0 | 2013-01-02 | 1.0 | 3 | train | foo |
2 | 1.0 | 2013-01-02 | 1.0 | 3 | test | foo |
3 | 1.0 | 2013-01-02 | 1.0 | 3 | train | foo |
# 生成的 DataFrame 的列具有不同的 dtype:
df2.dtypes
A float64
B datetime64[ns]
C float32
D int32
E category
F object
dtype: object
如果您正在使用 IPython,列名(以及公共属性)的 tab 补全会自动启用。在这个例子中,您可以使用 df2. 后跟 tab 键来查看可以自动补全的属性列表。
In [12] df2.<TAB>
df2.A df2.bool
df2.abs df2.boxplot
df2.add df2.C
df2.add_prefix df2.clip
df2.add_suffix df2.columns
df2.align df2.copy
df2.all df2.count
df2.any df2.combine
df2.append df2.D
df2.apply df2.describe
df2.applymap df2.diff
df2.B df2.duplicated
在这个例子中,列 A, B, C, 和 D 是可以自动补全的属性。E 和 F 也在列表中,但是为了简洁起见,其余的属性被截断了。
2.2 查看数据
您可以使用 DataFrame.head()
和 DataFrame.tail()
分别查看 DataFrame 的前几行和后几行:
df.head()
A | B | C | D | |
---|---|---|---|---|
2013-01-01 | 0.732649 | 0.452911 | 0.050417 | 0.234816 |
2013-01-02 | -1.127360 | 1.024067 | 2.355411 | 0.834325 |
2013-01-03 | -0.346012 | -1.059302 | -0.927076 | -0.518720 |
2013-01-04 | 0.793778 | -0.880850 | -0.140322 | 1.771748 |
2013-01-05 | -0.465872 | 0.074124 | -0.884347 | -0.125452 |
df.tail(3)
A | B | C | D | |
---|---|---|---|---|
2013-01-04 | 0.793778 | -0.880850 | -0.140322 | 1.771748 |
2013-01-05 | -0.465872 | 0.074124 | -0.884347 | -0.125452 |
2013-01-06 | 0.865522 | -0.773847 | -1.754687 | -1.171656 |
使用 DataFrame.index 或 DataFrame.columns 显示 DataFrame 的索引或列:
df.index
DatetimeIndex(['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04',
'2013-01-05', '2013-01-06'],
dtype='datetime64[ns]', freq='D')
df.columns
Index(['A', 'B', 'C', 'D'], dtype='object')
DataFrame.to_numpy()
给出了底层数据的 NumPy 表示。
请注意,当 DataFrame 具有不同数据类型的列时,这可能是一个昂贵的操作,
这归结于 pandas 和 NumPy 之间的根本差异:NumPy 数组对于整个数组只有一个 dtype,而 pandas DataFrame 对于每个列都有一个 dtype。当
您调用 DataFrame.to_numpy()
时,pandas 将找到可以容纳 DataFrame 中所有 dtype 的 NumPy dtype。
这可能最终是 object,这需要将每个值转换为 Python 对象。
对于 df
,DataFrame 中全是浮点数,DataFrame.to_numpy() 很快,不需要复制数据:
from time import time
import datetime
start1 = time()
df.to_numpy()
time1 = time()-start1
interval1 = datetime.datetime.fromtimestamp(time1).strftime("%M:%S:%f") # 查看计算所用时间
interval1
'00:00:000313'
对于 df2
,DataFrame 中有许多类型,DataFrame.to_numpy() 是个昂贵的操作:
start2 = time()
df2.to_numpy()
time2 = time()-start2
interval2 = datetime.datetime.fromtimestamp(time2).strftime("%M:%S:%f") # 查看计算所用时间
print(interval2)
print(f'所用时间是上一步的 {np.divide(time2, time1):.3f} 倍')
00:00:002456
所用时间是上一步的 7.846 倍
注意
DataFrame.to_numpy() 在输出中不包含索引或列标签。
2.3 选择数据
# 我们可以使用不同的方式来选择 DataFrame 中的数据。例如,选择一个单独的列:
df['A']
2013-01-01 0.732649
2013-01-02 -1.127360
2013-01-03 -0.346012
2013-01-04 0.793778
2013-01-05 -0.465872
2013-01-06 0.865522
Freq: D, Name: A, dtype: float64
# 或者选择行的切片:
df[0:3]
A | B | C | D | |
---|---|---|---|---|
2013-01-01 | 0.732649 | 0.452911 | 0.050417 | 0.234816 |
2013-01-02 | -1.127360 | 1.024067 | 2.355411 | 0.834325 |
2013-01-03 | -0.346012 | -1.059302 | -0.927076 | -0.518720 |
# 或者通过标签选择:
df.loc[dates[0]]
A 0.732649
B 0.452911
C 0.050417
D 0.234816
Name: 2013-01-01 00:00:00, dtype: float64
# 或者通过标签在多个轴上选择:
df.loc[:, ['A', 'B']]
A | B | |
---|---|---|
2013-01-01 | 0.732649 | 0.452911 |
2013-01-02 | -1.127360 | 1.024067 |
2013-01-03 | -0.346012 | -1.059302 |
2013-01-04 | 0.793778 | -0.880850 |
2013-01-05 | -0.465872 | 0.074124 |
2013-01-06 | 0.865522 | -0.773847 |
# 或者通过位置选择:
df.iloc[3]
A 0.793778
B -0.880850
C -0.140322
D 1.771748
Name: 2013-01-04 00:00:00, dtype: float64
# 或者通过位置在多个轴上选择:
df.iloc[3:5, 0:2]
A | B | |
---|---|---|
2013-01-04 | 0.793778 | -0.880850 |
2013-01-05 | -0.465872 | 0.074124 |
# 或者通过一个布尔数组来选择:
df[df.A > 0]
A | B | C | D | |
---|---|---|---|---|
2013-01-01 | 0.732649 | 0.452911 | 0.050417 | 0.234816 |
2013-01-04 | 0.793778 | -0.880850 | -0.140322 | 1.771748 |
2013-01-06 | 0.865522 | -0.773847 | -1.754687 | -1.171656 |
# 或者通过 isin() 方法来过滤:
df2 = df.copy()
df2['E'] = ['one', 'one', 'two', 'three', 'four', 'three']
df2[df2['E'].isin(['two', 'four'])]
A | B | C | D | E | |
---|---|---|---|---|---|
2013-01-03 | -0.346012 | -1.059302 | -0.927076 | -0.518720 | two |
2013-01-05 | -0.465872 | 0.074124 | -0.884347 | -0.125452 | four |
2.4 缺失值操作
pandas提供了一些方法来处理缺失值。要删除缺失值,您可以使用以下代码:
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randn(5, 3), index=['a', 'c', 'e', 'f', 'h'], columns=['one', 'two', 'three'])
df = df.reindex(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])
print(df)
print('-------------------------------')
print(df.dropna())
one two three
a 0.228386 0.244739 -1.583778
b NaN NaN NaN
c 0.292575 -0.115925 -0.140590
d NaN NaN NaN
e 0.367375 0.202992 -0.190355
f -0.581880 1.325593 1.040521
g NaN NaN NaN
h -1.566352 -1.547750 0.614942
-------------------------------
one two three
a 0.228386 0.244739 -1.583778
c 0.292575 -0.115925 -0.140590
e 0.367375 0.202992 -0.190355
f -0.581880 1.325593 1.040521
h -1.566352 -1.547750 0.614942
这将创建一个DataFrame,其中包含一些随机数字和NaN值。然后,它将删除所有包含NaN值的行。
要替换缺失值,您可以使用以下代码:
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randn(5, 3), index=['a', 'c', 'e', 'f', 'h'], columns=['one', 'two', 'three'])
df = df.reindex(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])
print(df)
print('-'*50)
print(df.fillna(0))
one two three
a 1.042368 0.745451 1.395693
b NaN NaN NaN
c -0.339838 0.632446 2.070172
d NaN NaN NaN
e -0.245898 0.858535 0.376770
f 0.792838 -0.569140 0.012835
g NaN NaN NaN
h 0.530681 -0.215863 -0.145303
--------------------------------------------------
one two three
a 1.042368 0.745451 1.395693
b 0.000000 0.000000 0.000000
c -0.339838 0.632446 2.070172
d 0.000000 0.000000 0.000000
e -0.245898 0.858535 0.376770
f 0.792838 -0.569140 0.012835
g 0.000000 0.000000 0.000000
h 0.530681 -0.215863 -0.145303
这将创建一个DataFrame,其中包含一些随机数字和NaN值。然后,它将使用0替换所有NaN值。
2.5 操作数据
2.5.1 统计
pandas有一些内置方法,可以很容易地计算统计信息,例如平均值,总和和最小值。默认情况下,这些方法排除缺失值。
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randn(5, 3), index=['a', 'c', 'e', 'f', 'h'], columns=['one', 'two', 'three'])
df = df.reindex(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])
print(df)
print('-'*50)
print(df.mean())
one two three
a -0.330677 -0.696890 0.177158
b NaN NaN NaN
c -0.770930 -1.848735 -1.065606
d NaN NaN NaN
e -0.664989 0.596805 -0.309245
f -0.510950 1.562430 1.541852
g NaN NaN NaN
h -0.038146 -0.082760 -1.090831
--------------------------------------------------
one -0.463138
two -0.093830
three -0.149334
dtype: float64
这将创建一个DataFrame,其中包含一些随机数字。然后,它将计算每列的平均值。
您还可以使用其他方法,例如sum,cumsum和describe:
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randn(5, 3), index=['a', 'c', 'e', 'f', 'h'], columns=['one', 'two', 'three'])
df = df.reindex(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'])
print(df)
print('-'*50)
print(df.sum())
print('-'*50)
print(df.cumsum())
print('-'*50)
print(df.describe())
one two three
a 1.094430 -1.051135 0.799771
b NaN NaN NaN
c -0.241605 -1.196541 -0.836143
d NaN NaN NaN
e 0.606664 0.187086 -0.293555
f 0.003697 1.398083 -0.864984
g NaN NaN NaN
h 0.089101 -1.088488 0.313684
--------------------------------------------------
one 1.552287
two -1.750996
three -0.881227
dtype: float64
--------------------------------------------------
one two three
a 1.094430 -1.051135 0.799771
b NaN NaN NaN
c 0.852825 -2.247676 -0.036372
d NaN NaN NaN
e 1.459489 -2.060591 -0.329927
f 1.463185 -0.662508 -1.194911
g NaN NaN NaN
h 1.552287 -1.750996 -0.881227
--------------------------------------------------
one two three
count 5.000000 5.000000 5.000000
mean 0.310457 -0.350199 -0.176245
std 0.536242 1.128920 0.727362
min -0.241605 -1.196541 -0.864984
25% 0.003697 -1.088488 -0.836143
50% 0.089101 -1.051135 -0.293555
75% 0.606664 0.187086 0.313684
max 1.094430 1.398083 0.799771
这将创建一个DataFrame,其中包含一些随机数字。然后,它将计算每列的总和,累积总和和描述性统计信息。
2.5.2 apply 方法
在 pandas 中,apply 方法是一种非常有用的方法,它可以将函数应用于 DataFrame 的行或列。例如,您可以使用 apply 方法计算每行或每列的总和,平均值或其他统计信息。
df = pd.DataFrame(np.random.randn(5, 3), index=['a', 'b', 'c', 'd', 'e'], columns=['one', 'two', 'three'])
print(df)
print('-' * 50)
print(df.apply(np.mean))
print('-' * 50)
print(df.apply(np.sum, axis=1))
one two three
a 1.836104 -0.413132 -1.327340
b 0.459524 0.484854 0.975912
c -0.139497 -0.821099 0.519994
d 0.940098 -0.732839 0.125682
e -0.244552 1.931906 0.761521
--------------------------------------------------
one 0.570335
two 0.089938
three 0.211154
dtype: float64
--------------------------------------------------
a 0.095632
b 1.920290
c -0.440601
d 0.332941
e 2.448875
dtype: float64
这将创建一个DataFrame,其中包含一些随机数字。然后,它将使用apply方法计算每列的平均值和每行的总和。
2.5.3 直方图
直方图可以用于直观地查看数据分布。 以下是如何使用 matplotlib 绘制直方图的示例。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 创建一个Series
s = pd.Series(np.random.randint(0, 7, size=10))
print(s.value_counts())
# 绘制直方图
s.hist(bins=20)
4 3
1 3
0 2
5 1
3 1
dtype: int64
<AxesSubplot:>
<Figure size 640x480 with 1 Axes>
在Series上调用hist
方法将自动绘制直方图。 可以指定bins
参数以控制直方图中的箱数。
请注意,hist
方法还可以在DataFrame上调用。 在这种情况下,每列的直方图将绘制在单独的子图中。
请访问直方图以获取更多信息。
2.5.4 字符串方法
Series对象在str属性中配备了一组字符串处理方法,可以轻松地对数组的每个元素进行操作,如下面的代码片段所示。
s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])
# 调用str方法
print(s)
print('-' * 50)
print(s.str.lower())
0 A
1 B
2 C
3 Aaba
4 Baca
5 NaN
6 CABA
7 dog
8 cat
dtype: object
--------------------------------------------------
0 a
1 b
2 c
3 aaba
4 baca
5 NaN
6 caba
7 dog
8 cat
dtype: object
请注意,当数据中存在NaN时,它们将自动跳过。
请访问字符串方法以获取更多信息。
2.6 合并表格
2.6.1 Concat
concat 是 pandas 中的一个函数,它可以将多个 pandas 对象(如 DataFrame 或 Series)沿着一个轴(通常是行轴)进行连接。
下面是一个简单的例子:
import pandas as pd
import numpy as np
df1 = pd.DataFrame(np.ones((3, 4)) * 0, columns=['a', 'b', 'c', 'd'])
df2 = pd.DataFrame(np.ones((3, 4)) * 1, columns=['a', 'b', 'c', 'd'])
df3 = pd.DataFrame(np.ones((3, 4)) * 2, columns=['a', 'b', 'c', 'd'])
res = pd.concat([df1, df2, df3], axis=0, ignore_index=True)
print(res)
a b c d
0 0.0 0.0 0.0 0.0
1 0.0 0.0 0.0 0.0
2 0.0 0.0 0.0 0.0
3 1.0 1.0 1.0 1.0
4 1.0 1.0 1.0 1.0
5 1.0 1.0 1.0 1.0
6 2.0 2.0 2.0 2.0
7 2.0 2.0 2.0 2.0
8 2.0 2.0 2.0 2.0
注意
在 pandas 中,向 DataFrame 添加列是相对较快的。但是,添加行需要进行复制,可能会很昂贵。我们建议将预先构建的记录列表传递给 DataFrame 构造函数,而不是通过迭代地将记录附加到 DataFrame 中来构建 DataFrame。
2.6.2 Join
在 Pandas 中,有两种主要的用于合并数据的方法:merge 和 join。
merge 函数允许您根据一个或多个键将不同 DataFrame 对象中的行连接起来。这种操作类似于 SQL 中的 JOIN 操作。
例如,假设我们有两个 DataFrame 对象:
left = pd.DataFrame({'key': ['foo', 'bar'], 'lval': [1, 2]})
right = pd.DataFrame({'key': ['foo', 'bar'], 'rval': [4, 5]})
print(left)
print('-' * 50)
print(right)
key lval
0 foo 1
1 bar 2
--------------------------------------------------
key rval
0 foo 4
1 bar 5
我们可以使用 merge 函数将它们合并在一起:
merged = pd.merge(left, right, on='key')
merged
key | lval | rval | |
---|---|---|---|
0 | foo | 1 | 4 |
1 | bar | 2 | 5 |
另一种常用的数据合并方法是 join。DataFrame 具有一个方便的 join 实例方法,它可以快速将不同 DataFrame 对象中具有相同或相似索引的行连接在一起。
例如,假设我们有两个 DataFrame 对象:
left = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
'B': ['B0', 'B1', 'B2']},
index=['K0', 'K1', 'K2'])
right = pd.DataFrame({'C': ['C0', 'C2', 'C3'],
'D': ['D0', 'D2', 'D3']},
index=['K0', 'K2', 'K3'])
left
A | B | |
---|---|---|
K0 | A0 | B0 |
K1 | A1 | B1 |
K2 | A2 | B2 |
right
C | D | |
---|---|---|
K0 | C0 | D0 |
K2 | C2 | D2 |
K3 | C3 | D3 |
我们可以使用 join 方法将它们连接在一起:
joined = left.join(right)
joined
A | B | C | D | |
---|---|---|---|---|
K0 | A0 | B0 | C0 | D0 |
K1 | A1 | B1 | NaN | NaN |
K2 | A2 | B2 | C2 | D2 |
2.7 数据分组
在 Pandas 中,要完成数据的分组操作,需要使用 groupby() 函数,它和 SQL 的 GROUP BY 操作非常相似。在划分出来的组(group)上应用一些统计函数,从而达到数据分析的目的,比如对分组数据进行聚合、转换,或者过滤。这个过程主要包含以下三步:
分割:按照一些规则将数据分成不同的组。
应用:对于每组数据分别执行一个函数。
合并:将每组的结果合并成一个数据结构。
下面是一个简单的例子:
df = pd.DataFrame({'A': ['foo', 'bar', 'foo', 'bar',
'foo', 'bar', 'foo', 'foo'],
'B': ['one', 'one', 'two', 'three',
'two', 'two', 'one', 'three'],
'C': np.random.randn(8),
'D': np.random.randn(8)})
df
A | B | C | D | |
---|---|---|---|---|
0 | foo | one | -1.250990 | -0.561155 |
1 | bar | one | 0.558040 | -0.715369 |
2 | foo | two | -1.176616 | 2.087695 |
3 | bar | three | -0.695511 | -1.438845 |
4 | foo | two | 0.712811 | -1.133881 |
5 | bar | two | 0.381369 | 0.166263 |
6 | foo | one | 0.881526 | 1.149048 |
7 | foo | three | -1.520854 | -1.484507 |
分组,然后将 sum() 函数应用于生成的组:
df.groupby("A")[["C", "D"]].sum()
C | D | |
---|---|---|
A | ||
bar | 0.243898 | -1.987951 |
foo | -2.354124 | 0.057199 |
按多列分组形成一个分层索引,我们可以再次应用 sum() 函数:
df.groupby(["A", "B"]).sum()
C | D | ||
---|---|---|---|
A | B | ||
bar | one | 0.558040 | -0.715369 |
three | -0.695511 | -1.438845 | |
two | 0.381369 | 0.166263 | |
foo | one | -0.369464 | 0.587893 |
three | -1.520854 | -1.484507 | |
two | -0.463805 | 0.953814 |
2.8 改变数据结构
好的,这是您要求的翻译:
在 Pandas 中,有几种方法可以对数据进行重塑和透视。
一种常用的方法是使用 stack 方法。此方法“压缩” DataFrame 的列级别,返回具有多层索引的 Series 对象。
例如,假设我们有一个 DataFrame 对象:
tuples = list(zip(*[['bar', 'bar', 'baz', 'baz',
'foo', 'foo', 'qux', 'qux'],
['one', 'two', 'one', 'two',
'one', 'two', 'one', 'two']]))
index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])
df = pd.DataFrame(np.random.randn(8, 2), index=index, columns=['A', 'B'])
df
A | B | ||
---|---|---|---|
first | second | ||
bar | one | -0.695612 | -0.253276 |
two | -1.457982 | 0.575581 | |
baz | one | -0.801558 | 1.118211 |
two | 0.486585 | 0.804642 | |
foo | one | 1.904547 | 0.520693 |
two | -0.925226 | 0.494819 | |
qux | one | 0.196789 | 1.234989 |
two | 0.575095 | -1.960254 |
我们可以使用 stack 方法将其转换为多层索引的 Series 对象:
stacked = df.stack()
stacked
first second
bar one A -0.695612
B -0.253276
two A -1.457982
B 0.575581
baz one A -0.801558
B 1.118211
two A 0.486585
B 0.804642
foo one A 1.904547
B 0.520693
two A -0.925226
B 0.494819
qux one A 0.196789
B 1.234989
two A 0.575095
B -1.960254
dtype: float64
另一种常用的重塑方法是使用 pivot 函数。此函数允许您根据给定列中的值创建新的 DataFrame,并使用另一列中的唯一值作为新 DataFrame 的列。
例如,假设我们有一个 DataFrame 对象:
df = pd.DataFrame(
{
"A": ["one", "one", "two", "three"] * 3,
"B": ["A", "B", "C"] * 4,
"C": ["foo", "foo", "foo", "bar", "bar", "bar"] * 2,
"D": np.random.randn(12),
"E": np.random.randn(12),
}
)
df
A | B | C | D | E | |
---|---|---|---|---|---|
0 | one | A | foo | -1.229208 | 1.632380 |
1 | one | B | foo | 0.707761 | -0.664045 |
2 | two | C | foo | 0.648949 | 1.175932 |
3 | three | A | bar | -0.740921 | -0.358632 |
4 | one | B | bar | -1.339020 | 0.585905 |
5 | one | C | bar | 0.456694 | 1.036741 |
6 | two | A | foo | 0.337801 | -0.642565 |
7 | three | B | foo | -0.548544 | -0.707633 |
8 | one | C | foo | -0.107039 | 0.311032 |
9 | one | A | bar | -1.555431 | 0.226431 |
10 | two | B | bar | 2.662271 | 0.630751 |
11 | three | C | bar | 0.372897 | 0.521987 |
我们可以使用 pivot 函数将其转换为新的 DataFrame:
pd.pivot_table(df, values="D", index=["A", "B"], columns=["C"])
C | bar | foo | |
---|---|---|---|
A | B | ||
one | A | -1.555431 | -1.229208 |
B | -1.339020 | 0.707761 | |
C | 0.456694 | -0.107039 | |
three | A | -0.740921 | NaN |
B | NaN | -0.548544 | |
C | 0.372897 | NaN | |
two | A | NaN | 0.337801 |
B | 2.662271 | NaN | |
C | NaN | 0.648949 |
2.9 时间序列
Pandas在执行频率转换(例如将秒数据转换为5分钟数据)期间具有简单,强大和高效的功能。这在金融应用中非常常见,但不仅限于此。您可以在Pandas文档的时间序列部分中找到更多信息。链接为https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html。
rng = pd.date_range("1/1/2012", periods=100, freq="S")
rng
DatetimeIndex(['2012-01-01 00:00:00', '2012-01-01 00:00:01',
'2012-01-01 00:00:02', '2012-01-01 00:00:03',
'2012-01-01 00:00:04', '2012-01-01 00:00:05',
'2012-01-01 00:00:06', '2012-01-01 00:00:07',
'2012-01-01 00:00:08', '2012-01-01 00:00:09',
'2012-01-01 00:00:10', '2012-01-01 00:00:11',
'2012-01-01 00:00:12', '2012-01-01 00:00:13',
'2012-01-01 00:00:14', '2012-01-01 00:00:15',
'2012-01-01 00:00:16', '2012-01-01 00:00:17',
'2012-01-01 00:00:18', '2012-01-01 00:00:19',
'2012-01-01 00:00:20', '2012-01-01 00:00:21',
'2012-01-01 00:00:22', '2012-01-01 00:00:23',
'2012-01-01 00:00:24', '2012-01-01 00:00:25',
'2012-01-01 00:00:26', '2012-01-01 00:00:27',
'2012-01-01 00:00:28', '2012-01-01 00:00:29',
'2012-01-01 00:00:30', '2012-01-01 00:00:31',
'2012-01-01 00:00:32', '2012-01-01 00:00:33',
'2012-01-01 00:00:34', '2012-01-01 00:00:35',
'2012-01-01 00:00:36', '2012-01-01 00:00:37',
'2012-01-01 00:00:38', '2012-01-01 00:00:39',
'2012-01-01 00:00:40', '2012-01-01 00:00:41',
'2012-01-01 00:00:42', '2012-01-01 00:00:43',
'2012-01-01 00:00:44', '2012-01-01 00:00:45',
'2012-01-01 00:00:46', '2012-01-01 00:00:47',
'2012-01-01 00:00:48', '2012-01-01 00:00:49',
'2012-01-01 00:00:50', '2012-01-01 00:00:51',
'2012-01-01 00:00:52', '2012-01-01 00:00:53',
'2012-01-01 00:00:54', '2012-01-01 00:00:55',
'2012-01-01 00:00:56', '2012-01-01 00:00:57',
'2012-01-01 00:00:58', '2012-01-01 00:00:59',
'2012-01-01 00:01:00', '2012-01-01 00:01:01',
'2012-01-01 00:01:02', '2012-01-01 00:01:03',
'2012-01-01 00:01:04', '2012-01-01 00:01:05',
'2012-01-01 00:01:06', '2012-01-01 00:01:07',
'2012-01-01 00:01:08', '2012-01-01 00:01:09',
'2012-01-01 00:01:10', '2012-01-01 00:01:11',
'2012-01-01 00:01:12', '2012-01-01 00:01:13',
'2012-01-01 00:01:14', '2012-01-01 00:01:15',
'2012-01-01 00:01:16', '2012-01-01 00:01:17',
'2012-01-01 00:01:18', '2012-01-01 00:01:19',
'2012-01-01 00:01:20', '2012-01-01 00:01:21',
'2012-01-01 00:01:22', '2012-01-01 00:01:23',
'2012-01-01 00:01:24', '2012-01-01 00:01:25',
'2012-01-01 00:01:26', '2012-01-01 00:01:27',
'2012-01-01 00:01:28', '2012-01-01 00:01:29',
'2012-01-01 00:01:30', '2012-01-01 00:01:31',
'2012-01-01 00:01:32', '2012-01-01 00:01:33',
'2012-01-01 00:01:34', '2012-01-01 00:01:35',
'2012-01-01 00:01:36', '2012-01-01 00:01:37',
'2012-01-01 00:01:38', '2012-01-01 00:01:39'],
dtype='datetime64[ns]', freq='S')
ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng)
ts
2012-01-01 00:00:00 343
2012-01-01 00:00:01 400
2012-01-01 00:00:02 408
2012-01-01 00:00:03 372
2012-01-01 00:00:04 475
...
2012-01-01 00:01:35 430
2012-01-01 00:01:36 472
2012-01-01 00:01:37 417
2012-01-01 00:01:38 273
2012-01-01 00:01:39 196
Freq: S, Length: 100, dtype: int64
ts.resample("5Min").sum()
2012-01-01 26038
Freq: 5T, dtype: int64
Series.tz_localize()
方法用于将 Series 或 DataFrame 的 tz-naive 索引本地化到目标时区。
rng = pd.date_range("3/6/2012 00:00", periods=5, freq="D")
ts = pd.Series(np.random.randn(len(rng)), rng)
ts_utc = ts.tz_localize("UTC")
ts_utc
2012-03-06 00:00:00+00:00 -0.182367
2012-03-07 00:00:00+00:00 0.137739
2012-03-08 00:00:00+00:00 -0.219611
2012-03-09 00:00:00+00:00 -0.447437
2012-03-10 00:00:00+00:00 -0.312121
Freq: D, dtype: float64
Series.tz_convert()
方法用于处理时区感知的索引。它将tz-aware轴转换为目标时区。
ts_utc.tz_convert("US/Eastern")
2012-03-05 19:00:00-05:00 -0.182367
2012-03-06 19:00:00-05:00 0.137739
2012-03-07 19:00:00-05:00 -0.219611
2012-03-08 19:00:00-05:00 -0.447437
2012-03-09 19:00:00-05:00 -0.312121
Freq: D, dtype: float64
Pandas使用Timestamp实例和DatetimeIndex实例来表示时间戳和时间戳序列。对于常规时间跨度,Pandas使用Period对象表示标量值,使用PeriodIndex表示跨度序列。
rng = pd.date_range("1/1/2012", periods=5, freq="M")
ts = pd.Series(np.random.randn(len(rng)), index=rng)
ts
2012-01-31 -0.364667
2012-02-29 -0.677385
2012-03-31 0.400009
2012-04-30 -0.459159
2012-05-31 -1.058427
Freq: M, dtype: float64
ps = ts.to_period()
ps
2012-01 -0.364667
2012-02 -0.677385
2012-03 0.400009
2012-04 -0.459159
2012-05 -1.058427
Freq: M, dtype: float64
ps.to_timestamp()
2012-01-01 -0.364667
2012-02-01 -0.677385
2012-03-01 0.400009
2012-04-01 -0.459159
2012-05-01 -1.058427
Freq: MS, dtype: float64
将周期和时间戳之间的转换可以使用一些方便的算术函数。在下面的示例中,我们将以11月结束的年度季度频率转换为季度结束后的下个月的9点钟。1。这里的周期是指Pandas中的Period对象,时间戳是指Pandas中的Timestamp对象
prng = pd.period_range("1990Q1", "2000Q4", freq="Q-NOV")
ts = pd.Series(np.random.randn(len(prng)), prng)
ts.index = (prng.asfreq("M", "e") + 1).asfreq("H", "s") + 9
ts.head()
1990-03-01 09:00 0.783871
1990-06-01 09:00 -0.067191
1990-09-01 09:00 0.459511
1990-12-01 09:00 2.081520
1991-03-01 09:00 1.226343
Freq: H, dtype: float64
2.10 分类数据
这一部分介绍了 Pandas 中的分类数据类型(Categorical data type)。分类数据类型可以用来表示类别变量,它包含有限数量的可能值。在 Pandas 中,你可以使用 pd.Categorical 来创建一个分类变量。
df = pd.DataFrame(
{"id": [1, 2, 3, 4, 5, 6], "raw_grade": ["a", "b", "b", "a", "a", "e"]}
)
df
id | raw_grade | |
---|---|---|
0 | 1 | a |
1 | 2 | b |
2 | 3 | b |
3 | 4 | a |
4 | 5 | a |
5 | 6 | e |
将原始成绩转换为分类数据类型:
df["grade"] = df["raw_grade"].astype("category")
df["grade"]
0 a
1 b
2 b
3 a
4 a
5 e
Name: grade, dtype: category
Categories (3, object): ['a', 'b', 'e']
将类别重命名为更有意义的名称:
new_categories = ["very good", "good", "very bad"]
df["grade"] = df["grade"].cat.rename_categories(new_categories)
df["grade"]
0 very good
1 good
2 good
3 very good
4 very good
5 very bad
Name: grade, dtype: category
Categories (3, object): ['very good', 'good', 'very bad']
对类别重新排序并同时添加缺少的类别(默认情况下,Series.cat() 下的方法返回一个新的 Series):
df["grade"] = df["grade"].cat.set_categories(
["very bad", "bad", "medium", "good", "very good"]
)
df['grade']
0 very good
1 good
2 good
3 very good
4 very good
5 very bad
Name: grade, dtype: category
Categories (5, object): ['very bad', 'bad', 'medium', 'good', 'very good']
排序是按类别中的顺序排序,而不是按词法顺序排序:
df.sort_values(by="grade")
id | raw_grade | grade | |
---|---|---|---|
5 | 6 | e | very bad |
1 | 2 | b | good |
2 | 3 | b | good |
0 | 1 | a | very good |
3 | 4 | a | very good |
4 | 5 | a | very good |
按类型分组还会显示空类别:
df.groupby("grade").size()
grade
very bad 1
bad 0
medium 0
good 2
very good 3
dtype: int64
2.11 绘图
请参阅绘图文档(https://pandas.pydata.org/docs/user_guide/visualization.html#visualization)。
我们使用标准约定来引用 matplotlib API:
import matplotlib.pyplot as plt
plt.close("all") # plt.close 方法用于关闭图形窗口:
ts = pd.Series(np.random.randn(1000), index=pd.date_range("1/1/2000", periods=1000))
ts = ts.cumsum()
ts.plot()
<AxesSubplot:>
<Figure size 640x480 with 1 Axes>
如果在 Jupyter Notebook 下运行,则绘图将显示在 plot() 上。否则,请使用 matplotlib.pyplot.show() 显示它,或使用 matplotlib.pyplot.savefig 将其写入文件。
plt.show()
在 DataFrame 上,plot() 方法可以方便地使用标签绘制所有列:
df = pd.DataFrame(
np.random.randn(1000, 4), index=ts.index, columns=["A", "B", "C", "D"]
)
df = df.cumsum()
plt.figure()
df.plot()
plt.legend(loc='best')
<matplotlib.legend.Legend at 0x7fea5088fc10>
<Figure size 640x480 with 0 Axes>
<Figure size 640x480 with 1 Axes>
2.12 读取与导出数据
CSV 文件
df.to_csv("foo.csv") # 写入 csv 文件:使用 DataFrame.to_csv()
pd.read_csv("foo.csv") # 从 csv 文件读取:使用 read_csv()
Unnamed: 0 | A | B | C | D | |
---|---|---|---|---|---|
0 | 2000-01-01 | 2.257795 | -1.077276 | -2.428040 | -0.873786 |
1 | 2000-01-02 | 2.901576 | -1.730733 | -2.182896 | -1.249288 |
2 | 2000-01-03 | 3.977965 | -2.370467 | -1.208194 | -0.218163 |
3 | 2000-01-04 | 4.211439 | -3.445451 | -2.245164 | -1.816434 |
4 | 2000-01-05 | 5.175889 | -3.970042 | -1.987473 | -1.450798 |
... | ... | ... | ... | ... | ... |
995 | 2002-09-22 | -15.295596 | -1.237624 | 8.237827 | -18.912241 |
996 | 2002-09-23 | -14.486983 | -1.080428 | 9.215596 | -18.097333 |
997 | 2002-09-24 | -15.461451 | -1.303721 | 8.642622 | -16.890929 |
998 | 2002-09-25 | -14.929007 | -1.375580 | 9.116000 | -16.790838 |
999 | 2002-09-26 | -14.999034 | -0.764591 | 10.325462 | -15.971727 |
1000 rows × 5 columns
Excel 文件
df.to_excel("foo.xlsx", sheet_name="Sheet1") # 使用 DataFrame.to_excel() 写入 excel 文件
pd.read_excel("foo.xlsx", "Sheet1", index_col=None, na_values=["NA"]) # 使用 read_excel() 从 excel 文件中读取
Unnamed: 0 | A | B | C | D | |
---|---|---|---|---|---|
0 | 2000-01-01 | 2.257795 | -1.077276 | -2.428040 | -0.873786 |
1 | 2000-01-02 | 2.901576 | -1.730733 | -2.182896 | -1.249288 |
2 | 2000-01-03 | 3.977965 | -2.370467 | -1.208194 | -0.218163 |
3 | 2000-01-04 | 4.211439 | -3.445451 | -2.245164 | -1.816434 |
4 | 2000-01-05 | 5.175889 | -3.970042 | -1.987473 | -1.450798 |
... | ... | ... | ... | ... | ... |
995 | 2002-09-22 | -15.295596 | -1.237624 | 8.237827 | -18.912241 |
996 | 2002-09-23 | -14.486983 | -1.080428 | 9.215596 | -18.097333 |
997 | 2002-09-24 | -15.461451 | -1.303721 | 8.642622 | -16.890929 |
998 | 2002-09-25 | -14.929007 | -1.375580 | 9.116000 | -16.790838 |
999 | 2002-09-26 | -14.999034 | -0.764591 | 10.325462 | -15.971727 |
1000 rows × 5 columns
# 所有程序运行完毕,删除临时文件和文件夹
import os
os.remove('foo.xlsx')
os.remove('foo.csv')
总结
在本教程中,您学习了在 Pandas 的一些基础方法。
具体而言,您了解到:
- 使用 Pandas 创建、读取或导出数据
- 查看或选择数据,对数据进行处理(选择,删除,分组,分类等)
- 表格处理,例如多表合并,改变表的数据结构
- 使用 Pandas 时间序列数据
- 结合 matplotlib 对 Pandas 数据进行绘图
你有什么问题吗? 欢迎与我们联系 bohrium@dp.tech 。
进一步阅读
如果您希望更深入学习 Pandas 及 Python 数据分析,本节提供有关该主题的更多资源。
书籍
- Python for Data Analysis, 2021.
Pandas 项目
参考
- https://pandas.pydata.org/docs/user_guide/10min.html#min
- https://pandas.pydata.org
- https://amzn.to/3DyLaJc
- https://github.com/pandas-dev/pandas