Python 数据分析(PYDA)第三版(三)(2)

简介: Python 数据分析(PYDA)第三版(三)

Python 数据分析(PYDA)第三版(三)(1)https://developer.aliyun.com/article/1482379

6.2 二进制数据格式

以二进制格式存储(或序列化)数据的一种简单方法是使用 Python 的内置pickle模块。所有 pandas 对象都有一个to_pickle方法,它以 pickle 格式将数据写入磁盘:

In [95]: frame = pd.read_csv("examples/ex1.csv")
In [96]: frame
Out[96]: 
 a   b   c   d message
0  1   2   3   4   hello
1  5   6   7   8   world
2  9  10  11  12     foo
In [97]: frame.to_pickle("examples/frame_pickle")

Pickle 文件通常只能在 Python 中读取。您可以直接使用内置的pickle读取存储在文件中的任何“pickled”对象,或者更方便地使用pandas.read_pickle

In [98]: pd.read_pickle("examples/frame_pickle")
Out[98]: 
 a   b   c   d message
0  1   2   3   4   hello
1  5   6   7   8   world
2  9  10  11  12     foo

注意

pickle仅建议作为短期存储格式。问题在于很难保证格式随时间稳定;今天使用 pickle 的对象可能无法在以后的库版本中解除 pickle。pandas 在可能的情况下尽力保持向后兼容性,但在将来的某个时候可能需要“破坏”pickle 格式。

pandas 内置支持其他几种开源二进制数据格式,例如 HDF5、ORC 和 Apache Parquet。例如,如果安装pyarrow包(conda install pyarrow),则可以使用pandas.read_parquet读取 Parquet 文件:

In [100]: fec = pd.read_parquet('datasets/fec/fec.parquet')

我将在 HDF5 格式使用中给出一些 HDF5 示例。我鼓励您探索不同的文件格式,看看它们的速度和对您的分析工作的适用性。

读取 Microsoft Excel 文件

pandas 还支持使用pandas.ExcelFile类或pandas.read_excel函数读取存储在 Excel 2003(及更高版本)文件中的表格数据。在内部,这些工具使用附加包xlrdopenpyxl来分别读取旧式 XLS 和新式 XLSX 文件。这些必须使用 pip 或 conda 单独安装,而不是从 pandas 安装:

conda install openpyxl xlrd

要使用pandas.ExcelFile,请通过传递路径到xlsxlsx文件来创建一个实例:

In [101]: xlsx = pd.ExcelFile("examples/ex1.xlsx")

此对象可以显示文件中可用工作表名称的列表:

In [102]: xlsx.sheet_names
Out[102]: ['Sheet1']

可以使用parse将工作表中存储的数据读入 DataFrame:

In [103]: xlsx.parse(sheet_name="Sheet1")
Out[103]: 
 Unnamed: 0  a   b   c   d message
0           0  1   2   3   4   hello
1           1  5   6   7   8   world
2           2  9  10  11  12     foo

此 Excel 表具有索引列,因此我们可以使用index_col参数指示:

In [104]: xlsx.parse(sheet_name="Sheet1", index_col=0)
Out[104]: 
 a   b   c   d message
0  1   2   3   4   hello
1  5   6   7   8   world
2  9  10  11  12     foo

如果要在一个文件中读取多个工作表,则创建pandas.ExcelFile会更快,但您也可以简单地将文件名传递给pandas.read_excel

In [105]: frame = pd.read_excel("examples/ex1.xlsx", sheet_name="Sheet1")
In [106]: frame
Out[106]: 
 Unnamed: 0  a   b   c   d message
0           0  1   2   3   4   hello
1           1  5   6   7   8   world
2           2  9  10  11  12     foo

要将 pandas 数据写入 Excel 格式,必须首先创建一个ExcelWriter,然后使用 pandas 对象的to_excel方法将数据写入其中:

In [107]: writer = pd.ExcelWriter("examples/ex2.xlsx")
In [108]: frame.to_excel(writer, "Sheet1")
In [109]: writer.close()

您还可以将文件路径传递给to_excel,避免使用ExcelWriter

In [110]: frame.to_excel("examples/ex2.xlsx")

使用 HDF5 格式

HDF5 是一种受尊敬的文件格式,用于存储大量科学数组数据。它作为一个 C 库可用,并且在许多其他语言中都有接口,包括 Java、Julia、MATLAB 和 Python。HDF5 中的“HDF”代表分层数据格式。每个 HDF5 文件可以存储多个数据集和支持的元数据。与更简单的格式相比,HDF5 支持各种压缩模式的即时压缩,使具有重复模式的数据能够更有效地存储。HDF5 可以是处理不适合内存的数据集的良好选择,因为您可以有效地读取和写入更大数组的小部分。

要开始使用 HDF5 和 pandas,您必须首先通过使用 conda 安装tables包来安装 PyTables:

conda install pytables

注意

请注意,PyTables 包在 PyPI 中称为“tables”,因此如果您使用 pip 安装,您将需要运行pip install tables

虽然可以直接使用 PyTables 或 h5py 库访问 HDF5 文件,但 pandas 提供了一个简化存储 Series 和 DataFrame 对象的高级接口。HDFStore类的工作方式类似于字典,并处理底层细节:

In [113]: frame = pd.DataFrame({"a": np.random.standard_normal(100)})
In [114]: store = pd.HDFStore("examples/mydata.h5")
In [115]: store["obj1"] = frame
In [116]: store["obj1_col"] = frame["a"]
In [117]: store
Out[117]: 
<class 'pandas.io.pytables.HDFStore'>
File path: examples/mydata.h5

然后可以使用相同类似字典的 API 检索 HDF5 文件中包含的对象:

In [118]: store["obj1"]
Out[118]: 
 a
0  -0.204708
1   0.478943
2  -0.519439
3  -0.555730
4   1.965781
..       ...
95  0.795253
96  0.118110
97 -0.748532
98  0.584970
99  0.152677
[100 rows x 1 columns]

HDFStore支持两种存储模式,"fixed""table"(默认为"fixed")。后者通常较慢,但支持使用特殊语法进行查询操作:

In [119]: store.put("obj2", frame, format="table")
In [120]: store.select("obj2", where=["index >= 10 and index <= 15"])
Out[120]: 
 a
10  1.007189
11 -1.296221
12  0.274992
13  0.228913
14  1.352917
15  0.886429
In [121]: store.close()

putstore["obj2"] = frame方法的显式版本,但允许我们设置其他选项,如存储格式。

pandas.read_hdf函数为您提供了这些工具的快捷方式:

In [122]: frame.to_hdf("examples/mydata.h5", "obj3", format="table")
In [123]: pd.read_hdf("examples/mydata.h5", "obj3", where=["index < 5"])
Out[123]: 
 a
0 -0.204708
1  0.478943
2 -0.519439
3 -0.555730
4  1.965781

如果您愿意,可以删除您创建的 HDF5 文件,方法如下:

In [124]: import os
In [125]: os.remove("examples/mydata.h5")

注意

如果您正在处理存储在远程服务器上的数据,如 Amazon S3 或 HDFS,使用设计用于分布式存储的不同二进制格式(如Apache Parquet)可能更合适。

如果您在本地处理大量数据,我建议您探索 PyTables 和 h5py,看看它们如何满足您的需求。由于许多数据分析问题受 I/O 限制(而不是 CPU 限制),使用 HDF5 等工具可以大大加速您的应用程序。

注意

HDF5 不是数据库。它最适合于一次写入,多次读取的数据集。虽然数据可以随时添加到文件中,但如果多个写入者同时这样做,文件可能会损坏。

6.3 与 Web API 交互

许多网站都有提供数据源的公共 API,可以通过 JSON 或其他格式提供数据。有许多方法可以从 Python 访问这些 API;我推荐的一种方法是requests,可以使用 pip 或 conda 进行安装:

conda install requests

要在 GitHub 上找到 pandas 的最近 30 个问题,我们可以使用附加的requests库进行GET HTTP 请求:

In [126]: import requests
In [127]: url = "https://api.github.com/repos/pandas-dev/pandas/issues"
In [128]: resp = requests.get(url)
In [129]: resp.raise_for_status()
In [130]: resp
Out[130]: <Response [200]>

在使用requests.get后,始终调用raise_for_status以检查 HTTP 错误是一个好习惯。

响应对象的json方法将返回一个包含解析后的 JSON 数据的 Python 对象,作为字典或列表(取决于返回的 JSON 是什么):

In [131]: data = resp.json()
In [132]: data[0]["title"]
Out[132]: 'BUG: DataFrame.pivot mutates empty index.name attribute with typing._L
iteralGenericAlias'

由于检索到的结果基于实时数据,当您运行此代码时,您看到的结果几乎肯定会有所不同。

data中的每个元素都是一个包含 GitHub 问题页面上找到的所有数据的字典(评论除外)。我们可以直接将data传递给pandas.DataFrame并提取感兴趣的字段:

In [133]: issues = pd.DataFrame(data, columns=["number", "title",
 .....:                                      "labels", "state"])
In [134]: issues
Out[134]: 
 number 
0    52629  \
1    52628 
2    52626 
3    52625 
4    52624 
..     ... 
25   52579 
26   52577 
27   52576 
28   52571 
29   52570 
 title 
0   BUG: DataFrame.pivot mutates empty index.name attribute with typing._Li...  \
1                                 DEPR: unused keywords in DTI/TDI construtors 
2                         ENH: Infer best datetime format from a random sample 
3            BUG: ArrowExtensionArray logical_op not working in all directions 
4              ENH: pandas.core.groupby.SeriesGroupBy.apply allow raw argument 
..                                                                         ... 
25                                     BUG: Axial inconsistency of pandas.diff 
26                  BUG: describe not respecting ArrowDtype in include/exclude 
27                  BUG: describe does not distinguish between Int64 and int64 
28  BUG: `pandas.DataFrame.replace` silently fails to replace category type... 
29     BUG: DataFrame.describe include/exclude do not work for arrow datatypes 
 labels 
0   [{'id': 76811, 'node_id': 'MDU6TGFiZWw3NjgxMQ==', 'url': 'https://api.g... \
1                                                                           [] 
2 [] 
3   [{'id': 76811, 'node_id': 'MDU6TGFiZWw3NjgxMQ==', 'url': 'https://api.g... 
4 [{'id': 76812, 'node_id': 'MDU6TGFiZWw3NjgxMg==', 'url': 'https://api.g... 
..                                                                         ... 
25  [{'id': 76811, 'node_id': 'MDU6TGFiZWw3NjgxMQ==', 'url': 'https://api.g... 
26 [{'id': 3303158446, 'node_id': 'MDU6TGFiZWwzMzAzMTU4NDQ2', 'url': 'http... 
27 [{'id': 76811, 'node_id': 'MDU6TGFiZWw3NjgxMQ==', 'url': 'https://api.g... 
28 [{'id': 76811, 'node_id': 'MDU6TGFiZWw3NjgxMQ==', 'url': 'https://api.g... 
29 [{'id': 76811, 'node_id': 'MDU6TGFiZWw3NjgxMQ==', 'url': 'https://api.g... 
 state 
0   open 
1   open 
2   open 
3   open 
4   open 
..   ... 
25  open 
26  open 
27  open 
28  open 
29  open 
[30 rows x 4 columns]

通过一些努力,您可以创建一些更高级的接口,用于常见的 Web API,返回 DataFrame 对象以便进行更方便的分析。

6.4 与数据库交互

在商业环境中,许多数据可能不存储在文本或 Excel 文件中。基于 SQL 的关系数据库(如 SQL Server、PostgreSQL 和 MySQL)被广泛使用,许多替代数据库也变得非常流行。数据库的选择通常取决于应用程序的性能、数据完整性和可扩展性需求。

pandas 有一些函数可以简化将 SQL 查询结果加载到 DataFrame 中。例如,我将使用 Python 内置的sqlite3驱动程序创建一个 SQLite3 数据库:

In [135]: import sqlite3
In [136]: query = """
 .....: CREATE TABLE test
 .....: (a VARCHAR(20), b VARCHAR(20),
 .....:  c REAL,        d INTEGER
 .....: );"""
In [137]: con = sqlite3.connect("mydata.sqlite")
In [138]: con.execute(query)
Out[138]: <sqlite3.Cursor at 0x188e40ac0>
In [139]: con.commit()

然后,插入一些数据行:

In [140]: data = [("Atlanta", "Georgia", 1.25, 6),
 .....:         ("Tallahassee", "Florida", 2.6, 3),
 .....:         ("Sacramento", "California", 1.7, 5)]
In [141]: stmt = "INSERT INTO test VALUES(?, ?, ?, ?)"
In [142]: con.executemany(stmt, data)
Out[142]: <sqlite3.Cursor at 0x188ed02c0>
In [143]: con.commit()

大多数 Python SQL 驱动程序在从表中选择数据时返回一个元组列表:

In [144]: cursor = con.execute("SELECT * FROM test")
In [145]: rows = cursor.fetchall()
In [146]: rows
Out[146]: 
[('Atlanta', 'Georgia', 1.25, 6),
 ('Tallahassee', 'Florida', 2.6, 3),
 ('Sacramento', 'California', 1.7, 5)]

您可以将元组列表传递给 DataFrame 构造函数,但还需要列名,这些列名包含在游标的description属性中。请注意,对于 SQLite3,游标的description仅提供列名(其他字段,这些字段是 Python 的数据库 API 规范的一部分,为None),但对于其他一些数据库驱动程序,提供了更多的列信息:

In [147]: cursor.description
Out[147]: 
(('a', None, None, None, None, None, None),
 ('b', None, None, None, None, None, None),
 ('c', None, None, None, None, None, None),
 ('d', None, None, None, None, None, None))
In [148]: pd.DataFrame(rows, columns=[x[0] for x in cursor.description])
Out[148]: 
 a           b     c  d
0      Atlanta     Georgia  1.25  6
1  Tallahassee     Florida  2.60  3
2   Sacramento  California  1.70  5

这是一种相当复杂的操作,您不希望每次查询数据库时都重复。SQLAlchemy 项目是一个流行的 Python SQL 工具包,它抽象了 SQL 数据库之间的许多常见差异。pandas 有一个read_sql函数,可以让您轻松地从通用的 SQLAlchemy 连接中读取数据。您可以像这样使用 conda 安装 SQLAlchemy:

conda install sqlalchemy
• 1

现在,我们将使用 SQLAlchemy 连接到相同的 SQLite 数据库,并从之前创建的表中读取数据:

In [149]: import sqlalchemy as sqla
In [150]: db = sqla.create_engine("sqlite:///mydata.sqlite")
In [151]: pd.read_sql("SELECT * FROM test", db)
Out[151]: 
 a           b     c  d
0      Atlanta     Georgia  1.25  6
1  Tallahassee     Florida  2.60  3
2   Sacramento  California  1.70  5

6.5 结论

获取数据通常是数据分析过程中的第一步。在本章中,我们已经介绍了一些有用的工具,这些工具应该可以帮助您入门。在接下来的章节中,我们将深入探讨数据整理、数据可视化、时间序列分析等主题。


  1. 完整列表请参见www.fdic.gov/bank/individual/failed/banklist.html

七、数据清理和准备

原文:wesmckinney.com/book/data-cleaning

译者:飞龙

协议:CC BY-NC-SA 4.0

此开放访问网络版本的《Python 数据分析第三版》现已作为印刷版和数字版的伴侣提供。如果您发现任何勘误,请在此处报告。请注意,由 Quarto 生成的本站点的某些方面与 O’Reilly 的印刷版和电子书版本的格式不同。

如果您发现本书的在线版本有用,请考虑订购纸质版无 DRM 的电子书以支持作者。本网站的内容不得复制或再生产。代码示例采用 MIT 许可,可在 GitHub 或 Gitee 上找到。

在进行数据分析和建模过程中,大量时间花费在数据准备上:加载、清理、转换和重新排列。这些任务通常被报告为占据分析师 80%或更多的时间。有时,文件或数据库中存储数据的方式并不适合特定任务。许多研究人员选择使用通用编程语言(如 Python、Perl、R 或 Java)或 Unix 文本处理工具(如 sed 或 awk)对数据进行自发处理,从一种形式转换为另一种形式。幸运的是,pandas 与内置的 Python 语言功能一起,为您提供了一套高级、灵活和快速的工具,使您能够将数据转换为正确的形式。

如果您发现在本书或 pandas 库中找不到的数据操作类型,请随时在 Python 邮件列表或 pandas GitHub 网站上分享您的用例。事实上,pandas 的设计和实现很大程度上是由真实应用程序的需求驱动的。

在本章中,我讨论了有关缺失数据、重复数据、字符串操作和其他一些分析数据转换的工具。在下一章中,我将专注于以各种方式组合和重新排列数据集。

7.1 处理缺失数据

缺失数据在许多数据分析应用中很常见。pandas 的目标之一是尽可能地使处理缺失数据变得轻松。例如,默认情况下,pandas 对象上的所有描述性统计都排除缺失数据。

pandas 对象中表示缺失数据的方式有些不完美,但对于大多数真实世界的用途来说是足够的。对于float64数据类型,pandas 使用浮点值NaN(Not a Number)表示缺失数据。

我们称之为标记值:当存在时,表示缺失(或)值:

In [14]: float_data = pd.Series([1.2, -3.5, np.nan, 0])
In [15]: float_data
Out[15]: 
0    1.2
1   -3.5
2    NaN
3    0.0
dtype: float64

isna方法为我们提供一个布尔 Series,其中值为空时为True

In [16]: float_data.isna()
Out[16]: 
0    False
1    False
2     True
3    False
dtype: bool

在 pandas 中,我们采用了 R 编程语言中使用的惯例,将缺失数据称为 NA,代表不可用。在统计应用中,NA 数据可能是不存在的数据,也可能是存在但未被观察到的数据(例如通过数据收集问题)。在清理数据进行分析时,通常重要的是对缺失数据本身进行分析,以识别数据收集问题或由缺失数据引起的数据潜在偏差。

内置的 Python None值也被视为 NA:

In [17]: string_data = pd.Series(["aardvark", np.nan, None, "avocado"])
In [18]: string_data
Out[18]: 
0    aardvark
1         NaN
2        None
3     avocado
dtype: object
In [19]: string_data.isna()
Out[19]: 
0    False
1     True
2     True
3    False
dtype: bool
In [20]: float_data = pd.Series([1, 2, None], dtype='float64')
In [21]: float_data
Out[21]: 
0    1.0
1    2.0
2    NaN
dtype: float64
In [22]: float_data.isna()
Out[22]: 
0    False
1    False
2     True
dtype: bool

pandas 项目已经尝试使处理缺失数据在不同数据类型之间保持一致。像pandas.isna这样的函数抽象了许多烦人的细节。请参阅表 7.1 以获取与处理缺失数据相关的一些函数列表。

表 7.1:NA 处理对象方法

方法 描述
dropna 根据每个标签的值是否具有缺失数据来过滤轴标签,对于可以容忍多少缺失数据有不同的阈值。
fillna 使用某个值或插值方法(如 "ffill""bfill")填充缺失数据。
isna 返回指示哪些值缺失/NA 的布尔值。
notna isna 的否定,对于非 NA 值返回 True,对于 NA 值返回 False

过滤缺失数据

有几种过滤缺失数据的方法。虽然您始终可以选择使用 pandas.isna 和布尔索引手动执行,但 dropna 可能会有所帮助。对于 Series,它返回仅具有非空数据和索引值的 Series:

In [23]: data = pd.Series([1, np.nan, 3.5, np.nan, 7])
In [24]: data.dropna()
Out[24]: 
0    1.0
2    3.5
4    7.0
dtype: float64

这与执行以下操作相同:

In [25]: data[data.notna()]
Out[25]: 
0    1.0
2    3.5
4    7.0
dtype: float64

对于 DataFrame 对象,有不同的方法可以删除缺失数据。您可能希望删除所有 NA 的行或列,或者仅删除包含任何 NA 的行或列。dropna 默认情况下会删除包含缺失值的任何行:

In [26]: data = pd.DataFrame([[1., 6.5, 3.], [1., np.nan, np.nan],
 ....:                      [np.nan, np.nan, np.nan], [np.nan, 6.5, 3.]])
In [27]: data
Out[27]: 
 0    1    2
0  1.0  6.5  3.0
1  1.0  NaN  NaN
2  NaN  NaN  NaN
3  NaN  6.5  3.0
In [28]: data.dropna()
Out[28]: 
 0    1    2
0  1.0  6.5  3.0

传递 how="all" 将仅删除所有 NA 的行:

In [29]: data.dropna(how="all")
Out[29]: 
 0    1    2
0  1.0  6.5  3.0
1  1.0  NaN  NaN
3  NaN  6.5  3.0

请记住,这些函数默认情况下返回新对象,不会修改原始对象的内容。

要以相同方式删除列,请传递 axis="columns"

In [30]: data[4] = np.nan
In [31]: data
Out[31]: 
 0    1    2   4
0  1.0  6.5  3.0 NaN
1  1.0  NaN  NaN NaN
2  NaN  NaN  NaN NaN
3  NaN  6.5  3.0 NaN
In [32]: data.dropna(axis="columns", how="all")
Out[32]: 
 0    1    2
0  1.0  6.5  3.0
1  1.0  NaN  NaN
2  NaN  NaN  NaN
3  NaN  6.5  3.0

假设您只想保留包含至多一定数量缺失观察的行。您可以使用 thresh 参数指示这一点:

In [33]: df = pd.DataFrame(np.random.standard_normal((7, 3)))
In [34]: df.iloc[:4, 1] = np.nan
In [35]: df.iloc[:2, 2] = np.nan
In [36]: df
Out[36]: 
 0         1         2
0 -0.204708       NaN       NaN
1 -0.555730       NaN       NaN
2  0.092908       NaN  0.769023
3  1.246435       NaN -1.296221
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741
In [37]: df.dropna()
Out[37]: 
 0         1         2
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741
In [38]: df.dropna(thresh=2)
Out[38]: 
 0         1         2
2  0.092908       NaN  0.769023
3  1.246435       NaN -1.296221
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741

填充缺失数据

与过滤缺失数据(并可能连同其他数据一起丢弃)不同,您可能希望以任意方式填补任意数量的“空洞”。对于大多数情况,fillna 方法是要使用的主要函数。通过使用常量调用 fillna 可以用该值替换缺失值:

In [39]: df.fillna(0)
Out[39]: 
 0         1         2
0 -0.204708  0.000000  0.000000
1 -0.555730  0.000000  0.000000
2  0.092908  0.000000  0.769023
3  1.246435  0.000000 -1.296221
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741

通过字典调用 fillna,您可以为每列使用不同的填充值:

In [40]: df.fillna({1: 0.5, 2: 0})
Out[40]: 
 0         1         2
0 -0.204708  0.500000  0.000000
1 -0.555730  0.500000  0.000000
2  0.092908  0.500000  0.769023
3  1.246435  0.500000 -1.296221
4  0.274992  0.228913  1.352917
5  0.886429 -2.001637 -0.371843
6  1.669025 -0.438570 -0.539741

可用于重新索引的相同插值方法(请参见 表 5.3)也可用于 fillna

In [41]: df = pd.DataFrame(np.random.standard_normal((6, 3)))
In [42]: df.iloc[2:, 1] = np.nan
In [43]: df.iloc[4:, 2] = np.nan
In [44]: df
Out[44]: 
 0         1         2
0  0.476985  3.248944 -1.021228
1 -0.577087  0.124121  0.302614
2  0.523772       NaN  1.343810
3 -0.713544       NaN -2.370232
4 -1.860761       NaN       NaN
5 -1.265934       NaN       NaN
In [45]: df.fillna(method="ffill")
Out[45]: 
 0         1         2
0  0.476985  3.248944 -1.021228
1 -0.577087  0.124121  0.302614
2  0.523772  0.124121  1.343810
3 -0.713544  0.124121 -2.370232
4 -1.860761  0.124121 -2.370232
5 -1.265934  0.124121 -2.370232
In [46]: df.fillna(method="ffill", limit=2)
Out[46]: 
 0         1         2
0  0.476985  3.248944 -1.021228
1 -0.577087  0.124121  0.302614
2  0.523772  0.124121  1.343810
3 -0.713544  0.124121 -2.370232
4 -1.860761       NaN -2.370232
5 -1.265934       NaN -2.370232

使用 fillna,您可以做很多其他事情,比如使用中位数或平均统计数据进行简单的数据填充:

In [47]: data = pd.Series([1., np.nan, 3.5, np.nan, 7])
In [48]: data.fillna(data.mean())
Out[48]: 
0    1.000000
1    3.833333
2    3.500000
3    3.833333
4    7.000000
dtype: float64

请参见 表 7.2 了解 fillna 函数参数的参考。

表 7.2:fillna 函数参数

参数 描述
value 用于填充缺失值的标量值或类似字典的对象
method 插值方法:可以是 "bfill"(向后填充)或 "ffill"(向前填充)之一;默认为 None
axis 填充的轴("index""columns");默认为 axis="index"
limit 对于向前和向后填充,最大连续填充周期数

7.2 数据转换

到目前为止,在本章中,我们一直关注处理缺失数据。过滤、清理和其他转换是另一类重要操作。

删除重复项

DataFrame 中可能会出现重复行,原因有很多。这里是一个例子:

In [49]: data = pd.DataFrame({"k1": ["one", "two"] * 3 + ["two"],
 ....:                      "k2": [1, 1, 2, 3, 3, 4, 4]})
In [50]: data
Out[50]: 
 k1  k2
0  one   1
1  two   1
2  one   2
3  two   3
4  one   3
5  two   4
6  two   4

DataFrame 方法 duplicated 返回一个布尔 Series,指示每行是否为重复行(其列值与较早行中的值完全相等):

In [51]: data.duplicated()
Out[51]: 
0    False
1    False
2    False
3    False
4    False
5    False
6     True
dtype: bool

相关地,drop_duplicates 返回一个 DataFrame,其中过滤掉 duplicated 数组为 False 的行:

In [52]: data.drop_duplicates()
Out[52]: 
 k1  k2
0  one   1
1  two   1
2  one   2
3  two   3
4  one   3
5  two   4

默认情况下,这两种方法都考虑所有列;或者,您可以指定任何子集来检测重复项。假设我们有一个额外的值列,并且只想基于 "k1" 列过滤重复项:

In [53]: data["v1"] = range(7)
In [54]: data
Out[54]: 
 k1  k2  v1
0  one   1   0
1  two   1   1
2  one   2   2
3  two   3   3
4  one   3   4
5  two   4   5
6  two   4   6
In [55]: data.drop_duplicates(subset=["k1"])
Out[55]: 
 k1  k2  v1
0  one   1   0
1  two   1   1

duplicateddrop_duplicates 默认保留第一个观察到的值组合。传递 keep="last" 将返回最后一个:

In [56]: data.drop_duplicates(["k1", "k2"], keep="last")
Out[56]: 
 k1  k2  v1
0  one   1   0
1  two   1   1
2  one   2   2
3  two   3   3
4  one   3   4
6  two   4   6

使用函数或映射转换数据

对于许多数据集,您可能希望根据数组、Series 或 DataFrame 中的值执行一些基于值的转换。考虑收集的关于各种肉类的假设数据:

In [57]: data = pd.DataFrame({"food": ["bacon", "pulled pork", "bacon",
 ....:                               "pastrami", "corned beef", "bacon",
 ....:                               "pastrami", "honey ham", "nova lox"],
 ....:                      "ounces": [4, 3, 12, 6, 7.5, 8, 3, 5, 6]})
In [58]: data
Out[58]: 
 food  ounces
0        bacon     4.0
1  pulled pork     3.0
2        bacon    12.0
3     pastrami     6.0
4  corned beef     7.5
5        bacon     8.0
6     pastrami     3.0
7    honey ham     5.0
8     nova lox     6.0

假设您想要添加一个指示每种食物来自哪种动物的列。让我们写下每种不同肉类到动物种类的映射:

meat_to_animal = {
 "bacon": "pig",
 "pulled pork": "pig",
 "pastrami": "cow",
 "corned beef": "cow",
 "honey ham": "pig",
 "nova lox": "salmon"
}

Series 上的 map 方法(也在 Ch 5.2.5: 函数应用和映射 中讨论)接受一个包含映射的函数或类似字典的对象,用于对值进行转换:

In [60]: data["animal"] = data["food"].map(meat_to_animal)
In [61]: data
Out[61]: 
 food  ounces  animal
0        bacon     4.0     pig
1  pulled pork     3.0     pig
2        bacon    12.0     pig
3     pastrami     6.0     cow
4  corned beef     7.5     cow
5        bacon     8.0     pig
6     pastrami     3.0     cow
7    honey ham     5.0     pig
8     nova lox     6.0  salmon

我们也可以传递一个执行所有工作的函数:

In [62]: def get_animal(x):
 ....:     return meat_to_animal[x]
In [63]: data["food"].map(get_animal)
Out[63]: 
0       pig
1       pig
2       pig
3       cow
4       cow
5       pig
6       cow
7       pig
8    salmon
Name: food, dtype: object

使用 map 是执行逐元素转换和其他数据清理相关操作的便捷方式。

替换值

使用 fillna 方法填充缺失数据是更一般的值替换的特殊情况。正如您已经看到的,map 可以用于修改对象中的一部分值,但 replace 提供了一种更简单、更灵活的方法。让我们考虑这个 Series:

In [64]: data = pd.Series([1., -999., 2., -999., -1000., 3.])
In [65]: data
Out[65]: 
0       1.0
1    -999.0
2       2.0
3    -999.0
4   -1000.0
5       3.0
dtype: float64

-999 值可能是缺失数据的标记值。要用 pandas 理解的 NA 值替换这些值,可以使用 replace,生成一个新的 Series:

In [66]: data.replace(-999, np.nan)
Out[66]: 
0       1.0
1       NaN
2       2.0
3       NaN
4   -1000.0
5       3.0
dtype: float64

如果您想一次替换多个值,可以传递一个列表,然后是替代值:

In [67]: data.replace([-999, -1000], np.nan)
Out[67]: 
0    1.0
1    NaN
2    2.0
3    NaN
4    NaN
5    3.0
dtype: float64

要为每个值使用不同的替代值,传递一个替代列表:

In [68]: data.replace([-999, -1000], [np.nan, 0])
Out[68]: 
0    1.0
1    NaN
2    2.0
3    NaN
4    0.0
5    3.0
dtype: float64

传递的参数也可以是一个字典:

In [69]: data.replace({-999: np.nan, -1000: 0})
Out[69]: 
0    1.0
1    NaN
2    2.0
3    NaN
4    0.0
5    3.0
dtype: float64

注意

data.replace 方法与 data.str.replace 是不同的,后者执行逐元素的字符串替换。我们将在本章后面的 Series 中查看这些字符串方法。

重命名轴索引

与 Series 中的值类似,轴标签也可以通过函数或某种形式的映射进行类似转换,以生成新的、不同标记的对象。您还可以在原地修改轴,而不创建新的数据结构。这是一个简单的例子:

In [70]: data = pd.DataFrame(np.arange(12).reshape((3, 4)),
 ....:                     index=["Ohio", "Colorado", "New York"],
 ....:                     columns=["one", "two", "three", "four"])

与 Series 一样,轴索引具有 map 方法:

In [71]: def transform(x):
 ....:     return x[:4].upper()
In [72]: data.index.map(transform)
Out[72]: Index(['OHIO', 'COLO', 'NEW '], dtype='object')

您可以分配给 index 属性,直接修改 DataFrame:

In [73]: data.index = data.index.map(transform)
In [74]: data
Out[74]: 
 one  two  three  four
OHIO    0    1      2     3
COLO    4    5      6     7
NEW     8    9     10    11

如果要创建一个转换后的数据集副本而不修改原始数据集,一个有用的方法是 rename

In [75]: data.rename(index=str.title, columns=str.upper)
Out[75]: 
 ONE  TWO  THREE  FOUR
Ohio    0    1      2     3
Colo    4    5      6     7
New     8    9     10    11

值得注意的是,rename 可以与类似字典的对象一起使用,为轴标签的子集提供新值:

In [76]: data.rename(index={"OHIO": "INDIANA"},
 ....:             columns={"three": "peekaboo"})
Out[76]: 
 one  two  peekaboo  four
INDIANA    0    1         2     3
COLO       4    5         6     7
NEW        8    9        10    11

rename 可以避免手动复制 DataFrame 并为其 indexcolumns 属性分配新值的繁琐工作。

Python 数据分析(PYDA)第三版(三)(3)https://developer.aliyun.com/article/1482382

相关文章
|
2天前
|
数据采集 数据可视化 数据挖掘
利用Python和Pandas库优化数据分析流程
在当今数据驱动的时代,数据分析已成为企业和个人决策的重要依据。Python作为一种强大且易于上手的编程语言,配合Pandas这一功能丰富的数据处理库,极大地简化了数据分析的流程。本文将探讨如何利用Python和Pandas库进行高效的数据清洗、转换、聚合以及可视化,从而优化数据分析的流程,提高数据分析的效率和准确性。
|
2天前
|
SQL 数据采集 数据挖掘
构建高效的Python数据处理流水线:使用Pandas和NumPy优化数据分析任务
在数据科学和分析领域,Python一直是最受欢迎的编程语言之一。本文将介绍如何通过使用Pandas和NumPy库构建高效的数据处理流水线,从而加速数据分析任务的执行。我们将讨论如何优化数据加载、清洗、转换和分析的过程,以及如何利用这些库中的强大功能来提高代码的性能和可维护性。
|
2天前
|
数据可视化 数据挖掘 BI
【Python】—— pandas 数据分析
【Python】—— pandas 数据分析
19 1
|
2天前
|
数据采集 数据可视化 数据挖掘
如何利用Python中的Pandas库进行数据分析和可视化
Python的Pandas库是一种功能强大的工具,可以用于数据分析和处理。本文将介绍如何使用Pandas库进行数据分析和可视化,包括数据导入、清洗、转换以及基本的统计分析和图表绘制。通过学习本文,读者将能够掌握利用Python中的Pandas库进行高效数据处理和可视化的技能。
|
2天前
|
机器学习/深度学习 数据可视化 算法
使用Python进行数据分析的5个必备技巧
【5月更文挑战第9天】本文介绍了Python数据分析的五个关键技巧:1) 使用Pandas进行数据处理和清洗;2) 利用NumPy进行高效数值计算;3) 通过Matplotlib和Seaborn创建可视化图表;4) 使用Scikit-learn执行机器学习任务;5) 在Jupyter Notebook中进行交互式分析和文档分享。这些技巧能提升数据分析的效率和准确性。
|
2天前
|
数据采集 数据可视化 数据挖掘
Python 与 PySpark数据分析实战指南:解锁数据洞见
Python 与 PySpark数据分析实战指南:解锁数据洞见
|
2天前
|
机器学习/深度学习 运维 算法
Python数据分析中的异常检测与处理方法
在Python数据分析中,异常数据是一个常见但又十分重要的问题。本文将介绍几种常见的异常检测与处理方法,包括基于统计学方法、机器学习方法以及深度学习方法。通过对异常数据的有效检测与处理,可以提高数据分析的准确性和可信度,从而更好地指导业务决策。
|
2天前
|
数据采集 数据可视化 数据挖掘
Python在数据分析中的强大应用
【5月更文挑战第5天】Python在数据驱动时代成为数据分析师首选工具,得益于其丰富的数据科学库(如NumPy、Pandas、Matplotlib、Seaborn和SciPy)。这些库支持数据清洗、探索、建模和可视化。Python在数据清洗、文本分析、Web数据抓取和大数据处理等方面有广泛应用,并因其易学性、强大社区和广泛适用性而备受青睐。未来,Python在数据分析领域的角色将更加重要。
|
2天前
|
机器学习/深度学习 算法 数据挖掘
【Python机器学习专栏】金融数据分析中的机器学习应用
【4月更文挑战第30天】本文探讨了机器学习在金融数据分析中的应用,如股价预测、信用评分、欺诈检测、算法交易和风险管理,并以Python为例展示了如何进行股价预测。通过使用机器学习模型,金融机构能更准确地评估风险、识别欺诈行为并优化交易策略。Python结合scikit-learn库简化了数据分析过程,助力金融从业者提高决策效率。随着技术发展,机器学习在金融领域的影响力将持续增强。
|
2天前
|
数据采集 SQL 数据挖掘
Python数据分析中的Pandas库应用指南
在数据科学和分析领域,Python语言已经成为了一种非常流行的工具。本文将介绍Python中的Pandas库,该库提供了强大的数据结构和数据分析工具,使得数据处理变得更加简单高效。通过详细的示例和应用指南,读者将了解到如何使用Pandas库进行数据加载、清洗、转换和分析,从而提升数据处理的效率和准确性。