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

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

六、数据加载、存储和文件格式

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

译者:飞龙

协议:CC BY-NC-SA 4.0

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

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

读取数据并使其可访问(通常称为数据加载)是使用本书中大多数工具的必要第一步。术语解析有时也用于描述加载文本数据并将其解释为表格和不同数据类型。我将专注于使用 pandas 进行数据输入和输出,尽管其他库中有许多工具可帮助读取和写入各种格式的数据。

输入和输出通常分为几个主要类别:读取文本文件和其他更高效的磁盘格式、从数据库加载数据以及与网络源(如 Web API)交互。

6.1 以文本格式读取和写入数据

pandas 提供了许多函数,用于将表格数据读取为 DataFrame 对象。表 6.1 总结了其中一些;pandas.read_csv是本书中最常用的之一。我们将在二进制数据格式中稍后查看二进制数据格式。

表 6.1:pandas 中的文本和二进制数据加载函数

函数 描述
read_csv 从文件、URL 或类似文件的对象中加载分隔数据;使用逗号作为默认分隔符
read_fwf 以固定宽度列格式读取数据(即没有分隔符)
read_clipboard 读取剪贴板中的数据的read_csv变体;用于将网页上的表格转换的有用工具
read_excel 从 Excel XLS 或 XLSX 文件中读取表格数据
read_hdf 读取 pandas 写入的 HDF5 文件
read_html 读取给定 HTML 文档中找到的所有表格
read_json 从 JSON(JavaScript 对象表示)字符串表示、文件、URL 或类似文件的对象中读取数据
read_feather 读取 Feather 二进制文件格式
read_orc 读取 Apache ORC 二进制文件格式
read_parquet 读取 Apache Parquet 二进制文件格式
read_pickle 使用 Python pickle 格式读取由 pandas 存储的对象
read_sas 读取存储在 SAS 系统的自定义存储格式之一中的 SAS 数据集
read_spss 读取由 SPSS 创建的数据文件
read_sql 读取 SQL 查询的结果(使用 SQLAlchemy)
read_sql_table 读取整个 SQL 表(使用 SQLAlchemy);等同于使用选择该表中的所有内容的查询使用read_sql
read_stata 从 Stata 文件格式中读取数据集
read_xml 从 XML 文件中读取数据表

我将概述这些函数的机制,这些函数旨在将文本数据转换为 DataFrame。这些函数的可选参数可能属于几个类别:

索引

可以将一个或多个列视为返回的 DataFrame,并确定是否从文件、您提供的参数或根本不获取列名。

类型推断和数据转换

包括用户定义的值转换和自定义缺失值标记列表。

日期和时间解析

包括一种组合能力,包括将分布在多个列中的日期和时间信息组合成结果中的单个列。

迭代

支持迭代处理非常大文件的块。

不干净的数据问题

包括跳过行或页脚、注释或其他像数字数据以逗号分隔的小事物。

由于现实世界中的数据可能会很混乱,一些数据加载函数(特别是pandas.read_csv)随着时间的推移积累了很长的可选参数列表。对于不同参数的数量感到不知所措是正常的(pandas.read_csv大约有 50 个)。在线 pandas 文档有许多关于每个参数如何工作的示例,因此如果您在阅读特定文件时感到困惑,可能会有足够相似的示例帮助您找到正确的参数。

其中一些函数执行类型推断,因为列数据类型不是数据格式的一部分。这意味着您不一定需要指定哪些列是数字、整数、布尔值或字符串。其他数据格式,如 HDF5、ORC 和 Parquet,将数据类型信息嵌入到格式中。

处理日期和其他自定义类型可能需要额外的努力。

让我们从一个小的逗号分隔值(CSV)文本文件开始:

In [10]: !cat examples/ex1.csv
a,b,c,d,message
1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo

注意

这里我使用了 Unix 的cat shell 命令将文件的原始内容打印到屏幕上。如果您使用 Windows,可以在 Windows 终端(或命令行)中使用type代替cat来实现相同的效果。

由于这是逗号分隔的,我们可以使用pandas.read_csv将其读入 DataFrame:

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

文件不总是有标题行。考虑这个文件:

In [13]: !cat examples/ex2.csv
1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo

要读取此文件,您有几个选项。您可以允许 pandas 分配默认列名,或者您可以自己指定名称:

In [14]: pd.read_csv("examples/ex2.csv", header=None)
Out[14]: 
 0   1   2   3      4
0  1   2   3   4  hello
1  5   6   7   8  world
2  9  10  11  12    foo
In [15]: pd.read_csv("examples/ex2.csv", names=["a", "b", "c", "d", "message"])
Out[15]: 
 a   b   c   d message
0  1   2   3   4   hello
1  5   6   7   8   world
2  9  10  11  12     foo

假设您希望message列成为返回的 DataFrame 的索引。您可以使用index_col参数指示您希望在索引 4 处或使用名称"message"

In [16]: names = ["a", "b", "c", "d", "message"]
In [17]: pd.read_csv("examples/ex2.csv", names=names, index_col="message")
Out[17]: 
 a   b   c   d
message 
hello    1   2   3   4
world    5   6   7   8
foo      9  10  11  12

如果要从多个列创建分层索引(在 Ch 8.1:分层索引中讨论),请传递列编号或名称的列表:

In [18]: !cat examples/csv_mindex.csv
key1,key2,value1,value2
one,a,1,2
one,b,3,4
one,c,5,6
one,d,7,8
two,a,9,10
two,b,11,12
two,c,13,14
two,d,15,16
In [19]: parsed = pd.read_csv("examples/csv_mindex.csv",
 ....:                      index_col=["key1", "key2"])
In [20]: parsed
Out[20]: 
 value1  value2
key1 key2 
one  a          1       2
 b          3       4
 c          5       6
 d          7       8
two  a          9      10
 b         11      12
 c         13      14
 d         15      16

在某些情况下,表格可能没有固定的分隔符,而是使用空格或其他模式来分隔字段。考虑一个看起来像这样的文本文件:

In [21]: !cat examples/ex3.txt
A         B         C
aaa -0.264438 -1.026059 -0.619500
bbb  0.927272  0.302904 -0.032399
ccc -0.264273 -0.386314 -0.217601
ddd -0.871858 -0.348382  1.100491

虽然您可以手动进行一些数据处理,但这里的字段是由可变数量的空格分隔的。在这些情况下,您可以将正则表达式作为pandas.read_csv的分隔符传递。这可以通过正则表达式\s+表示,因此我们有:

In [22]: result = pd.read_csv("examples/ex3.txt", sep="\s+")
In [23]: result
Out[23]: 
 A         B         C
aaa -0.264438 -1.026059 -0.619500
bbb  0.927272  0.302904 -0.032399
ccc -0.264273 -0.386314 -0.217601
ddd -0.871858 -0.348382  1.100491

由于列名比数据行数少一个,pandas.read_csv推断在这种特殊情况下第一列应该是 DataFrame 的索引。

文件解析函数有许多额外的参数,可帮助您处理发生的各种异常文件格式(请参见表 6.2 中的部分列表)。例如,您可以使用skiprows跳过文件的第一、第三和第四行:

In [24]: !cat examples/ex4.csv
# hey!
a,b,c,d,message
# just wanted to make things more difficult for you
# who reads CSV files with computers, anyway?
1,2,3,4,hello
5,6,7,8,world
9,10,11,12,foo
In [25]: pd.read_csv("examples/ex4.csv", skiprows=[0, 2, 3])
Out[25]: 
 a   b   c   d message
0  1   2   3   4   hello
1  5   6   7   8   world
2  9  10  11  12     foo

处理缺失值是文件读取过程中重要且经常微妙的部分。缺失数据通常要么不存在(空字符串),要么由某个标记(占位符)值标记。默认情况下,pandas 使用一组常见的标记,例如NANULL

In [26]: !cat examples/ex5.csv
something,a,b,c,d,message
one,1,2,3,4,NA
two,5,6,,8,world
three,9,10,11,12,foo
In [27]: result = pd.read_csv("examples/ex5.csv")
In [28]: result
Out[28]: 
 something  a   b     c   d message
0       one  1   2   3.0   4     NaN
1       two  5   6   NaN   8   world
2     three  9  10  11.0  12     foo

请记住,pandas 将缺失值输出为NaN,因此在result中有两个空值或缺失值:

In [29]: pd.isna(result)
Out[29]: 
 something      a      b      c      d  message
0      False  False  False  False  False     True
1      False  False  False   True  False    False
2      False  False  False  False  False    False

na_values选项接受一个字符串序列,用于添加到默认识别为缺失的字符串列表中:

In [30]: result = pd.read_csv("examples/ex5.csv", na_values=["NULL"])
In [31]: result
Out[31]: 
 something  a   b     c   d message
0       one  1   2   3.0   4     NaN
1       two  5   6   NaN   8   world
2     three  9  10  11.0  12     foo

pandas.read_csv有许多默认的 NA 值表示列表,但这些默认值可以通过keep_default_na选项禁用:

In [32]: result2 = pd.read_csv("examples/ex5.csv", keep_default_na=False)
In [33]: result2
Out[33]: 
 something  a   b   c   d message
0       one  1   2   3   4      NA
1       two  5   6       8   world
2     three  9  10  11  12     foo
In [34]: result2.isna()
Out[34]: 
 something      a      b      c      d  message
0      False  False  False  False  False    False
1      False  False  False  False  False    False
2      False  False  False  False  False    False
In [35]: result3 = pd.read_csv("examples/ex5.csv", keep_default_na=False,
 ....:                       na_values=["NA"])
In [36]: result3
Out[36]: 
 something  a   b   c   d message
0       one  1   2   3   4     NaN
1       two  5   6       8   world
2     three  9  10  11  12     foo
In [37]: result3.isna()
Out[37]: 
 something      a      b      c      d  message
0      False  False  False  False  False     True
1      False  False  False  False  False    False
2      False  False  False  False  False    False

可以在字典中为每列指定不同的 NA 标记:

In [38]: sentinels = {"message": ["foo", "NA"], "something": ["two"]}
In [39]: pd.read_csv("examples/ex5.csv", na_values=sentinels,
 ....:             keep_default_na=False)
Out[39]: 
 something  a   b   c   d message
0       one  1   2   3   4     NaN
1       NaN  5   6       8   world
2     three  9  10  11  12     NaN

表 6.2 列出了pandas.read_csv中一些经常使用的选项。

表 6.2:一些pandas.read_csv函数参数

参数 描述
path 指示文件系统位置、URL 或类似文件的字符串。
sepdelimiter 用于在每行中拆分字段的字符序列或正则表达式。
header 用作列名的行号;默认为 0(第一行),但如果没有标题行,则应为None
index_col 用作结果中行索引的列号或名称;可以是单个名称/编号或用于分层索引的列表。
names 结果的列名列表。
skiprows 要忽略的文件开头的行数或要跳过的行号列表(从 0 开始)。
na_values 要替换为 NA 的值序列。除非传递keep_default_na=False,否则它们将添加到默认列表中。
keep_default_na 是否使用默认的 NA 值列表(默认为True)。
comment 用于将注释从行末分隔出来的字符。
parse_dates 尝试解析数据为datetime;默认为False。如果为True,将尝试解析所有列。否则,可以指定要解析的列号或名称的列表。如果列表的元素是元组或列表,则将多个列组合在一起并解析为日期(例如,如果日期/时间跨越两列)。
keep_date_col 如果连接列以解析日期,则保留连接的列;默认为False
converters 包含列号或名称映射到函数的字典(例如,{"foo": f}将对"foo"列中的所有值应用函数f)。
dayfirst 在解析可能模糊的日期时,将其视为国际格式(例如,7/6/2012 -> 2012 年 6 月 7 日);默认为False
date_parser 用于解析日期的函数。
nrows 从文件开头读取的行数(不包括标题)。
iterator 返回一个用于逐步读取文件的TextFileReader对象。此对象也可以与with语句一起使用。
chunksize 用于迭代的文件块的大小。
skip_footer 要忽略的文件末尾行数。
verbose 打印各种解析信息,如文件转换各阶段所花费的时间和内存使用信息。
encoding 文本编码(例如,UTF-8 编码文本的"utf-8")。如果为None,默认为"utf-8"
squeeze 如果解析的数据只包含一列,则返回一个 Series。
thousands 千位分隔符(例如,","".");默认为None
decimal 数字中的小数分隔符(例如,"."",");默认为"."
engine 要使用的 CSV 解析和转换引擎;可以是"c""python""pyarrow"之一。默认为"c",尽管较新的"pyarrow"引擎可以更快地解析一些文件。"python"引擎速度较慢,但支持其他引擎不支持的一些功能。

分块读取文本文件

在处理非常大的文件或找出正确的参数集以正确处理大文件时,您可能只想读取文件的一小部分或迭代文件的较小块。

在查看大文件之前,我们将 pandas 显示设置更加紧凑:

In [40]: pd.options.display.max_rows = 10

现在我们有:

In [41]: result = pd.read_csv("examples/ex6.csv")
In [42]: result
Out[42]: 
 one       two     three      four key
0     0.467976 -0.038649 -0.295344 -1.824726   L
1    -0.358893  1.404453  0.704965 -0.200638   B
2    -0.501840  0.659254 -0.421691 -0.057688   G
3     0.204886  1.074134  1.388361 -0.982404   R
4     0.354628 -0.133116  0.283763 -0.837063   Q
...        ...       ...       ...       ...  ..
9995  2.311896 -0.417070 -1.409599 -0.515821   L
9996 -0.479893 -0.650419  0.745152 -0.646038   E
9997  0.523331  0.787112  0.486066  1.093156   K
9998 -0.362559  0.598894 -1.843201  0.887292   G
9999 -0.096376 -1.012999 -0.657431 -0.573315   0
[10000 rows x 5 columns]

省略号...表示已省略数据框中间的行。

如果您只想读取少量行(避免读取整个文件),请使用nrows指定:

In [43]: pd.read_csv("examples/ex6.csv", nrows=5)
Out[43]: 
 one       two     three      four key
0  0.467976 -0.038649 -0.295344 -1.824726   L
1 -0.358893  1.404453  0.704965 -0.200638   B
2 -0.501840  0.659254 -0.421691 -0.057688   G
3  0.204886  1.074134  1.388361 -0.982404   R
4  0.354628 -0.133116  0.283763 -0.837063   Q

要分块读取文件,指定一个作为行数的chunksize

In [44]: chunker = pd.read_csv("examples/ex6.csv", chunksize=1000)
In [45]: type(chunker)
Out[45]: pandas.io.parsers.readers.TextFileReader

pandas.read_csv返回的TextFileReader对象允许您根据chunksize迭代文件的部分。例如,我们可以迭代ex6.csv,聚合"key"列中的值计数,如下所示:

chunker = pd.read_csv("examples/ex6.csv", chunksize=1000)
tot = pd.Series([], dtype='int64')
for piece in chunker:
 tot = tot.add(piece["key"].value_counts(), fill_value=0)
tot = tot.sort_values(ascending=False)

然后我们有:

In [47]: tot[:10]
Out[47]: 
key
E    368.0
X    364.0
L    346.0
O    343.0
Q    340.0
M    338.0
J    337.0
F    335.0
K    334.0
H    330.0
dtype: float64

TextFileReader还配备有一个get_chunk方法,使您能够以任意大小读取文件的片段。

将数据写入文本格式

数据也可以导出为分隔格式。让我们考虑之前读取的一个 CSV 文件:

In [48]: data = pd.read_csv("examples/ex5.csv")
In [49]: data
Out[49]: 
 something  a   b     c   d message
0       one  1   2   3.0   4     NaN
1       two  5   6   NaN   8   world
2     three  9  10  11.0  12     foo

使用 DataFrame 的 to_csv 方法,我们可以将数据写入逗号分隔的文件:

In [50]: data.to_csv("examples/out.csv")
In [51]: !cat examples/out.csv
,something,a,b,c,d,message
0,one,1,2,3.0,4,
1,two,5,6,,8,world
2,three,9,10,11.0,12,foo

当然也可以使用其他分隔符(写入到 sys.stdout 以便将文本结果打印到控制台而不是文件):

In [52]: import sys
In [53]: data.to_csv(sys.stdout, sep="|")
|something|a|b|c|d|message
0|one|1|2|3.0|4|
1|two|5|6||8|world
2|three|9|10|11.0|12|foo

缺失值在输出中显示为空字符串。您可能希望用其他标记值来表示它们:

In [54]: data.to_csv(sys.stdout, na_rep="NULL")
,something,a,b,c,d,message
0,one,1,2,3.0,4,NULL
1,two,5,6,NULL,8,world
2,three,9,10,11.0,12,foo

如果未指定其他选项,则将同时写入行标签和列标签。这两者都可以禁用:

In [55]: data.to_csv(sys.stdout, index=False, header=False)
one,1,2,3.0,4,
two,5,6,,8,world
three,9,10,11.0,12,foo

您还可以仅写入列的子集,并按您选择的顺序进行写入:

In [56]: data.to_csv(sys.stdout, index=False, columns=["a", "b", "c"])
a,b,c
1,2,3.0
5,6,
9,10,11.0

处理其他分隔格式

使用函数如 pandas.read_csv 可以从磁盘加载大多数形式的表格数据。然而,在某些情况下,可能需要一些手动处理。接收到一个或多个格式错误的行可能会导致 pandas.read_csv 出错。为了说明基本工具,考虑一个小的 CSV 文件:

In [57]: !cat examples/ex7.csv
"a","b","c"
"1","2","3"
"1","2","3"

对于任何具有单字符分隔符的文件,您可以使用 Python 的内置 csv 模块。要使用它,将任何打开的文件或类似文件的对象传递给 csv.reader

In [58]: import csv
In [59]: f = open("examples/ex7.csv")
In [60]: reader = csv.reader(f)

像处理文件一样迭代读取器会产生去除任何引号字符的值列表:

In [61]: for line in reader:
 ....:     print(line)
['a', 'b', 'c']
['1', '2', '3']
['1', '2', '3']
In [62]: f.close()

然后,您需要进行必要的整理以将数据放入所需的形式。让我们一步一步来。首先,我们将文件读取为行列表:

In [63]: with open("examples/ex7.csv") as f:
 ....:     lines = list(csv.reader(f))

然后我们将行分割为标题行和数据行:

In [64]: header, values = lines[0], lines[1:]

然后我们可以使用字典推导和表达式 zip(*values) 创建数据列的字典(请注意,这将在大文件上使用大量内存),将行转置为列:

In [65]: data_dict = {h: v for h, v in zip(header, zip(*values))}
In [66]: data_dict
Out[66]: {'a': ('1', '1'), 'b': ('2', '2'), 'c': ('3', '3')}

CSV 文件有许多不同的风格。要定义一个具有不同分隔符、字符串引用约定或行终止符的新格式,我们可以定义一个简单的 csv.Dialect 的子类:

class my_dialect(csv.Dialect):
 lineterminator = "\n"
 delimiter = ";"
 quotechar = '"'
 quoting = csv.QUOTE_MINIMAL
reader = csv.reader(f, dialect=my_dialect)

我们还可以将单独的 CSV 方言参数作为关键字传递给 csv.reader,而无需定义子类:

reader = csv.reader(f, delimiter="|")

可能的选项(csv.Dialect 的属性)及其作用可以在 表 6.3 中找到。

表 6.3: CSV dialect 选项

参数 描述
delimiter 用于分隔字段的单字符字符串;默认为 ","
lineterminator 用于写入的行终止符;默认为 "\r\n"。读取器会忽略这个并识别跨平台的行终止符。
quotechar 用于具有特殊字符(如分隔符)的字段的引用字符;默认为 '"'
quoting 引用约定。选项包括 csv.QUOTE_ALL(引用所有字段)、csv.QUOTE_MINIMAL(只有包含特殊字符如分隔符的字段)、csv.QUOTE_NONNUMERICcsv.QUOTE_NONE(不引用)。详细信息请参阅 Python 的文档。默认为 QUOTE_MINIMAL
skipinitialspace 忽略每个分隔符后的空格;默认为 False
doublequote 如何处理字段内的引用字符;如果为 True,则会加倍(请查看在线文档以获取完整的详细信息和行为)。
escapechar 如果 quoting 设置为 csv.QUOTE_NONE,用于转义分隔符的字符串;默认情况下禁用。

注意

对于具有更复杂或固定多字符分隔符的文件,您将无法使用 csv 模块。在这些情况下,您将需要使用字符串的 split 方法或正则表达式方法 re.split 进行行分割和其他清理。幸运的是,如果传递必要的选项,pandas.read_csv 能够几乎做任何您需要的事情,因此您很少需要手动解析文件。

手动 写入分隔文件,可以使用 csv.writer。它接受一个打开的可写文件对象以及与 csv.reader 相同的方言和格式选项:

with open("mydata.csv", "w") as f:
 writer = csv.writer(f, dialect=my_dialect)
 writer.writerow(("one", "two", "three"))
 writer.writerow(("1", "2", "3"))
 writer.writerow(("4", "5", "6"))
 writer.writerow(("7", "8", "9"))

JSON 数据

JSON(JavaScript 对象表示法的缩写)已经成为在 Web 浏览器和其他应用程序之间通过 HTTP 请求发送数据的标准格式之一。它是比 CSV 等表格文本形式更自由的数据格式。这里是一个例子:

obj = """
{"name": "Wes",
 "cities_lived": ["Akron", "Nashville", "New York", "San Francisco"],
 "pet": null,
 "siblings": [{"name": "Scott", "age": 34, "hobbies": ["guitars", "soccer"]},
 {"name": "Katie", "age": 42, "hobbies": ["diving", "art"]}]
}
"""

JSON 几乎是有效的 Python 代码,只是其空值null和一些其他细微差别(例如不允许在列表末尾使用逗号)。基本类型是对象(字典)、数组(列表)、字符串、数字、布尔值和空值。对象中的所有键都必须是字符串。有几个 Python 库可用于读取和写入 JSON 数据。我将在这里使用json,因为它内置在 Python 标准库中。要将 JSON 字符串转换为 Python 形式,请使用json.loads

In [68]: import json
In [69]: result = json.loads(obj)
In [70]: result
Out[70]: 
{'name': 'Wes',
 'cities_lived': ['Akron', 'Nashville', 'New York', 'San Francisco'],
 'pet': None,
 'siblings': [{'name': 'Scott',
 'age': 34,
 'hobbies': ['guitars', 'soccer']},
 {'name': 'Katie', 'age': 42, 'hobbies': ['diving', 'art']}]}

json.dumps,另一方面,将 Python 对象转换回 JSON:

In [71]: asjson = json.dumps(result)
In [72]: asjson
Out[72]: '{"name": "Wes", "cities_lived": ["Akron", "Nashville", "New York", "San
 Francisco"], "pet": null, "siblings": [{"name": "Scott", "age": 34, "hobbies": [
"guitars", "soccer"]}, {"name": "Katie", "age": 42, "hobbies": ["diving", "art"]}
]}'

如何将 JSON 对象或对象列表转换为 DataFrame 或其他数据结构以进行分析将取决于您。方便的是,您可以将字典列表(先前是 JSON 对象)传递给 DataFrame 构造函数并选择数据字段的子集:

In [73]: siblings = pd.DataFrame(result["siblings"], columns=["name", "age"])
In [74]: siblings
Out[74]: 
 name  age
0  Scott   34
1  Katie   42

pandas.read_json可以自动将特定排列的 JSON 数据集转换为 Series 或 DataFrame。例如:

In [75]: !cat examples/example.json
[{"a": 1, "b": 2, "c": 3},
 {"a": 4, "b": 5, "c": 6},
 {"a": 7, "b": 8, "c": 9}]

pandas.read_json的默认选项假定 JSON 数组中的每个对象是表中的一行:

In [76]: data = pd.read_json("examples/example.json")
In [77]: data
Out[77]: 
 a  b  c
0  1  2  3
1  4  5  6
2  7  8  9

有关阅读和操作 JSON 数据的扩展示例(包括嵌套记录),请参见第十三章:数据分析示例中的美国农业部食品数据库示例。

如果您需要将数据从 pandas 导出为 JSON,一种方法是在 Series 和 DataFrame 上使用to_json方法:

In [78]: data.to_json(sys.stdout)
{"a":{"0":1,"1":4,"2":7},"b":{"0":2,"1":5,"2":8},"c":{"0":3,"1":6,"2":9}}
In [79]: data.to_json(sys.stdout, orient="records")
[{"a":1,"b":2,"c":3},{"a":4,"b":5,"c":6},{"a":7,"b":8,"c":9}]

XML 和 HTML:网络抓取

Python 有许多用于读取和写入 HTML 和 XML 格式数据的库。示例包括 lxml、Beautiful Soup 和 html5lib。虽然 lxml 通常在一般情况下更快,但其他库可以更好地处理格式不正确的 HTML 或 XML 文件。

pandas 有一个内置函数pandas.read_html,它使用所有这些库自动将 HTML 文件中的表格解析为 DataFrame 对象。为了展示这是如何工作的,我下载了一个 HTML 文件(在 pandas 文档中使用)从美国联邦存款保险公司显示银行倒闭。¹首先,您必须安装一些read_html使用的附加库:

conda install lxml beautifulsoup4 html5lib

如果您没有使用 conda,pip install lxml也应该可以工作。

pandas.read_html函数有许多选项,但默认情况下它会搜索并尝试解析包含在标签中的所有表格数据。结果是一个 DataFrame 对象的列表:

In [80]: tables = pd.read_html("examples/fdic_failed_bank_list.html")
In [81]: len(tables)
Out[81]: 1
In [82]: failures = tables[0]
In [83]: failures.head()
Out[83]: 
 Bank Name             City  ST   CERT 
0                   Allied Bank         Mulberry  AR     91  \
1  The Woodbury Banking Company         Woodbury  GA  11297 
2        First CornerStone Bank  King of Prussia  PA  35312 
3            Trust Company Bank          Memphis  TN   9956 
4    North Milwaukee State Bank        Milwaukee  WI  20364 
 Acquiring Institution        Closing Date       Updated Date 
0                         Today's Bank  September 23, 2016  November 17, 2016 
1                          United Bank     August 19, 2016  November 17, 2016 
2  First-Citizens Bank & Trust Company         May 6, 2016  September 6, 2016 
3           The Bank of Fayette County      April 29, 2016  September 6, 2016 
4  First-Citizens Bank & Trust Company      March 11, 2016      June 16, 2016 

由于failures有许多列,pandas 会插入一个换行符\

正如您将在后面的章节中了解到的那样,从这里我们可以继续进行一些数据清理和分析,比如计算每年的银行倒闭次数:

In [84]: close_timestamps = pd.to_datetime(failures["Closing Date"])
In [85]: close_timestamps.dt.year.value_counts()
Out[85]: 
Closing Date
2010    157
2009    140
2011     92
2012     51
2008     25
 ... 
2004      4
2001      4
2007      3
2003      3
2000      2
Name: count, Length: 15, dtype: int64
使用lxml.objectify解析 XML

XML 是另一种常见的结构化数据格式,支持具有元数据的分层嵌套数据。您当前正在阅读的书实际上是从一系列大型 XML 文档创建的。

之前,我展示了pandas.read_html函数,它在底层使用 lxml 或 Beautiful Soup 来解析 HTML 中的数据。XML 和 HTML 在结构上相似,但 XML 更通用。在这里,我将展示如何使用 lxml 来解析更一般的 XML 格式中的数据的示例。

多年来,纽约大都会交通管理局(MTA)以 XML 格式发布了许多关于其公交车和火车服务的数据系列。在这里,我们将查看性能数据,这些数据包含在一组 XML 文件中。每个火车或公交车服务都有一个不同的文件(例如Performance_MNR.xml用于 Metro-North Railroad),其中包含作为一系列 XML 记录的月度数据,看起来像这样:

<INDICATOR>
 <INDICATOR_SEQ>373889</INDICATOR_SEQ>
 <PARENT_SEQ></PARENT_SEQ>
 <AGENCY_NAME>Metro-North Railroad</AGENCY_NAME>
 <INDICATOR_NAME>Escalator Availability</INDICATOR_NAME>
 <DESCRIPTION>Percent of the time that escalators are operational
 systemwide. The availability rate is based on physical observations performed
 the morning of regular business days only. This is a new indicator the agency
 began reporting in 2009.</DESCRIPTION>
 <PERIOD_YEAR>2011</PERIOD_YEAR>
 <PERIOD_MONTH>12</PERIOD_MONTH>
 <CATEGORY>Service Indicators</CATEGORY>
 <FREQUENCY>M</FREQUENCY>
 <DESIRED_CHANGE>U</DESIRED_CHANGE>
 <INDICATOR_UNIT>%</INDICATOR_UNIT>
 <DECIMAL_PLACES>1</DECIMAL_PLACES>
 <YTD_TARGET>97.00</YTD_TARGET>
 <YTD_ACTUAL></YTD_ACTUAL>
 <MONTHLY_TARGET>97.00</MONTHLY_TARGET>
 <MONTHLY_ACTUAL></MONTHLY_ACTUAL>
</INDICATOR>

使用lxml.objectify,我们解析文件并获取 XML 文件的根节点的引用:

In [86]: from lxml import objectify
In [87]: path = "datasets/mta_perf/Performance_MNR.xml"
In [88]: with open(path) as f:
 ....:     parsed = objectify.parse(f)
In [89]: root = parsed.getroot()

root.INDICATOR返回一个生成器,产生每个 XML 元素。对于每条记录,我们可以通过运行以下代码填充一个标签名称(如YTD_ACTUAL)到数据值(排除一些标签)的字典:

data = []
skip_fields = ["PARENT_SEQ", "INDICATOR_SEQ",
 "DESIRED_CHANGE", "DECIMAL_PLACES"]
for elt in root.INDICATOR:
 el_data = {}
 for child in elt.getchildren():
 if child.tag in skip_fields:
 continue
 el_data[child.tag] = child.pyval
 data.append(el_data)

最后,将这个字典列表转换为 DataFrame:

In [91]: perf = pd.DataFrame(data)
In [92]: perf.head()
Out[92]: 
 AGENCY_NAME                        INDICATOR_NAME 
0  Metro-North Railroad  On-Time Performance (West of Hudson)  \
1  Metro-North Railroad  On-Time Performance (West of Hudson) 
2  Metro-North Railroad  On-Time Performance (West of Hudson) 
3  Metro-North Railroad  On-Time Performance (West of Hudson) 
4  Metro-North Railroad  On-Time Performance (West of Hudson) 
 DESCRIPTION 
0  Percent of commuter trains that arrive at their destinations within 5 m...  \
1  Percent of commuter trains that arrive at their destinations within 5 m... 
2  Percent of commuter trains that arrive at their destinations within 5 m... 
3  Percent of commuter trains that arrive at their destinations within 5 m... 
4  Percent of commuter trains that arrive at their destinations within 5 m... 
 PERIOD_YEAR  PERIOD_MONTH            CATEGORY FREQUENCY INDICATOR_UNIT 
0         2008             1  Service Indicators         M              %  \
1         2008             2  Service Indicators         M              % 
2         2008             3  Service Indicators         M              % 
3         2008             4  Service Indicators         M              % 
4         2008             5  Service Indicators         M              % 
 YTD_TARGET YTD_ACTUAL MONTHLY_TARGET MONTHLY_ACTUAL 
0       95.0       96.9           95.0           96.9 
1       95.0       96.0           95.0           95.0 
2       95.0       96.3           95.0           96.9 
3       95.0       96.8           95.0           98.3 
4       95.0       96.6           95.0           95.8 

pandas 的pandas.read_xml函数将此过程转换为一行表达式:

In [93]: perf2 = pd.read_xml(path)
In [94]: perf2.head()
Out[94]: 
 INDICATOR_SEQ  PARENT_SEQ           AGENCY_NAME 
0          28445         NaN  Metro-North Railroad  \
1          28445         NaN  Metro-North Railroad 
2          28445         NaN  Metro-North Railroad 
3          28445         NaN  Metro-North Railroad 
4          28445         NaN  Metro-North Railroad 
 INDICATOR_NAME 
0  On-Time Performance (West of Hudson)  \
1  On-Time Performance (West of Hudson) 
2  On-Time Performance (West of Hudson) 
3  On-Time Performance (West of Hudson) 
4  On-Time Performance (West of Hudson) 
 DESCRIPTION 
0  Percent of commuter trains that arrive at their destinations within 5 m...  \
1  Percent of commuter trains that arrive at their destinations within 5 m... 
2  Percent of commuter trains that arrive at their destinations within 5 m... 
3  Percent of commuter trains that arrive at their destinations within 5 m... 
4  Percent of commuter trains that arrive at their destinations within 5 m... 
 PERIOD_YEAR  PERIOD_MONTH            CATEGORY FREQUENCY DESIRED_CHANGE 
0         2008             1  Service Indicators         M              U  \
1         2008             2  Service Indicators         M              U 
2         2008             3  Service Indicators         M              U 
3         2008             4  Service Indicators         M              U 
4         2008             5  Service Indicators         M              U 
 INDICATOR_UNIT  DECIMAL_PLACES YTD_TARGET YTD_ACTUAL MONTHLY_TARGET 
0              %               1      95.00      96.90          95.00  \
1              %               1      95.00      96.00          95.00 
2              %               1      95.00      96.30          95.00 
3              %               1      95.00      96.80          95.00 
4              %               1      95.00      96.60          95.00 
 MONTHLY_ACTUAL 
0          96.90 
1          95.00 
2          96.90 
3          98.30 
4          95.80 

对于更复杂的 XML 文档,请参考pandas.read_xml的文档字符串,其中描述了如何进行选择和过滤以提取感兴趣的特定表格。


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


相关文章
|
22小时前
|
数据采集 人工智能 数据挖掘
「一行分析」利用12000条招聘数据分析Python学习方向和就业方向
「一行分析」利用12000条招聘数据分析Python学习方向和就业方向
|
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天前
|
网络协议 Unix Python
Python编程-----网络通信
Python编程-----网络通信
8 1
|
2天前
|
JSON 数据格式 开发者
pip和requests在Python编程中各自扮演着不同的角色
【5月更文挑战第9天】`pip`是Python的包管理器,用于安装、升级和管理PyPI上的包;`requests`是一个HTTP库,简化了HTTP通信,支持各种HTTP请求类型及数据交互。两者在Python环境中分别负责包管理和网络请求。
32 5
|
2天前
|
存储 Python 容器
Python高级编程
Python集合包括可变的set和不可变的frozenset,用于存储无序、不重复的哈希元素。创建集合可使用{}或set(),如`my_set = {1, 2, 3, 4, 5}`。通过add()添加元素,remove()或discard()删除元素,如`my_set.remove(3)`。
14 0