常见数据文件存储和读取

3808 字 · 453 阅读 · 2023 年 06 月 09 日

本文已更新,你可以访问 AI By Doing 以获得更好的阅读体验。
本篇文章需 特别授权许可,内容版权归作者所有,未经授权,禁止转载。

介绍

数据分析虽然强调的是分析过程和结果,但是「数据」却是必不可少的基础。没有数据,何谈数据分析?同时,数据分析师手中的数据也并不是凭空出现的,往往采集数据的工作都需要亲力亲为。所以,首先我们就需要了解常见的数据采集方法和手段。

知识点

  • 数据文件类型
  • 数据文件读取
  • 数据文件存储
  • JSON 解析
  • 数据分块读取

引言

一个完整的数据分析和挖掘流程,通常会经历 4 个大的步骤,分别是:数据采集读取 → 数据清洁及预处理 → 数据挖掘建模 → 数据分析报告。你可以看到,数据采集读取是整个分析流程的第一步,也是非常重要的一步。因为,数据是不可能凭空出现的。只有手中有数据,我们才可以执行后面的步骤。

当然,这只是一个大致的流程,并不严谨。相信随着学习的深入,你会对完整的数据分析流程细节更加清楚。

一般情况下,肯定是先采集数据,存储数据,再读取数据。但是本阶段的内容中,我们会先介绍如何读取和存储数据,再学习常用采集数据的方法。究其原因,是想让大家先建立起对数据的基本认识。

很多时候,是我们手中已经有了现成的数据,再决定对数据进行分析,这种情况多见于内部数据分析任务。这种情况下,我们已经有数据文件或数据库,只需要把相应的数据读取出来即可。

常用数据文件格式

数据文件是非常常见的数据储存方式,适合于小规模数据。例如,公司通过调查问卷的方式收集了大量的数据,一般导出之后就是一个数据文件。再例如,数据分析人员一般没有权限直接连接公司数据库,那么你拿到手中的数据就可能是数据库管理员给你导出之后的数据文件。

当我们使用 Python 读取数据文件时,首先推荐的就是通过 Pandas 完成,Pandas 几乎支持所有常见的数据文件格式。

数据类型 文件格式 读取方式 存储方式
文本 CSV read_csv to_csv
文本 JSON read_json to_json
文本 HTML read_html to_html
文本 剪切板 read_clipboard to_clipboard
二进制 MS Excel read_excel to_excel
二进制 HDF5 格式 read_hdf to_hdf
二进制 Feather 格式 read_feather to_feather
二进制 Parquet 格式 read_parquet to_parquet
二进制 Msgpack read_msgpack to_msgpack
二进制 Stata read_stata to_stata
二进制 SAS read_sas to_sas
二进制 Python Pickle 格式 read_pickle to_pickle

Excel 和 CSV 格式

上面的表格中,相信你最熟悉的是 Excel 表格数据文件。由于 Excel 表格有最大的行数储存限制(16,384 列 × 1,048,576 行),所以更多时候我们会使用 CSV 来储存表格数据。

CSV 的英文是 Comma-Separated Values,其实就是通过字符分割数据并以纯文本形式存储。这里的分割字符我们一般会使用逗号,所以往往也称 CSV 文件为逗号分隔符文件。纯文本意味着该文件是一个字符序列,不含必须像二进制数字那样被解读的数据,也没有最大行数的储存限制。

下面,我们尝试读取 Excel 和 CSV 格式的示例数据文件。首先,我们需要生成不同类型的数据示例文件。下面这段代码直接点击运行即可,将会在目录下方生成两个最常用的数据文件 test.csvtest.xlsx

教学代码

import numpy as np
import pandas as pd

# 生成示例数据
df = pd.DataFrame({'A': np.random.randn(10), 'B': np.random.randn(10)})

# 写入数据文件
df.to_csv('test.csv', index=None)  # CSV
df.to_excel('test.xlsx', index=None)  # EXCEL
print("*****示例文件写入成功*****")

动手练习

# 推荐运行 Jupyter Notebook 练习代码

下面,我们学习使用 Pandas 读取文件的方法,直接运用上面表格中的 API 即可。CSV 文件读取是 read_csv ,而 Excel 文件读取是 read_excel

pd.read_csv("test.csv")

当你读取 Excel 文件时,首先需要安装 openpyxl 模块,不然就会报错。安装该模块的命令为:pip install openpyxl。在线环境中,我们已经为你安装好了该模块。

pd.read_excel('test.xlsx')

你可以发现,通过 Pandas 读取处理的数据会自动以表格样式呈现,这也就是 Pandas 特有的数据格式:DataFrame。在学习数据分析的生涯中,DataFrame 将会成为你最容易听到的名词之一。Pandas 的所有数据操作都是针对于 DataFrame 进行。

特别地,我们会将 Pandas 中的一维数据类型称之为 Series,三维数据称之为 Panel。但实际上,它们都可以看作是特殊的 DataFrame。

HDF5 格式

HDF(英语:Hierarchical Data Format)指一种为存储和处理大容量科学数据设计的文件格式及相应库文件。HDF5 格式的特点在于能存储多个数据集,并且支持 metadata

HDF5 文件包含两种基本数据对象:

  • 群组(group):类似文件夹,可以包含多个数据集或下级群组。
  • 数据集(dataset):数据内容,可以是多维数组,也可以是更复杂的数据类型。

群组和数据集都支持元数据 metadata,用户可以自定义其属性,提供附加信息。元数据类似于「数据的数据」,它能够用来说明数据的特征和其他属性。

HDF5 的好处在于,你不仅可以使用 Python 存储和读取,目前还被 Java,MATLAB/Scilab,Octave,IDL,Julia, R 等语言或商业软件支持。

下面,我们同样尝试使用 Pandas 来存储和读取 HDF5 数据。和 Excel 文件读取相似,我们需要先安装一个依赖模块 PyTables,命令为:pip install tables

df1 = pd.DataFrame({'A': np.random.randn(10), 'B': np.random.randn(10)})  # 随机数据
df2 = pd.DataFrame({'C': np.random.randn(10), 'D': np.random.randn(10)})  # 随机数据

df1.to_hdf('test.h5', key='df1')  # 存储 df1
df2.to_hdf('test.h5', key='df2', format='table')  # 存储 df2

上面的代码中,我们通过指定 key 向 HDF 文件中存储了 2 个不同的数据集 df1df2。那么,接下来我们尝试读取。

pd.read_hdf('test.h5', key='df1')  # 读取 df1

如果你仔细观察,你会发现上面我们在存储示例数据时,df2 后面指定了 format='table' 参数。这是因为,HDF 支持两种存储架构:fixed 和 table。默认为 fixed,因为其读取速度更快,但是 table 却支持查询操作。

# 读取 df2 中 index < 5 的数据
pd.read_hdf('test.h5', key='df2', where=['index < 5'])

你可能会觉得,HDF5 既然支持存储多个数据集,是不是类似于数据库中的「表」呢?值得注意的是,HDF5 并不是数据库,如果多个使用者同时写入数据,数据文件会遭到破坏。

JSON 格式

JSON(英文:JavaScript Object Notation)是一种由道格拉斯·克罗克福特构想和设计、轻量级的数据交换语言,该语言以易于让人阅读的文字为基础,用来传输由属性值或者序列性的值组成的数据对象。尽管 JSON 是 JavaScript 的一个子集,但 JSON 是独立于语言的文本格式。

JSON 数据格式与语言无关,脱胎于 JavaScript,但目前很多编程语言都支持 JSON 格式数据的生成和解析。JSON 的官方 MIME 类型是 application/json,文件扩展名是 .json

这里特别说到 JSON 格式,原因是其已经成为了 HTTP 请求过程中的标准数据格式。而后面的采集数据过程中,我们会学习到通过 API 请求数据,一般都会对 JSON 进行解析。所以,这里先行了解学习。

下面,我们给出一段 JSON 的示例数据:

obj = """
[
    {
        "aqi": 46,
        "area": "成都",
        "pm2_5": 32,
        "pm2_5_24h": 33,
        "position_name": "金泉两河",
        "primary_pollutant": null,
        "quality": "优",
        "station_code": "1431A",
        "time_point": "2018-09-05T09:00:00Z"
    },
    {
        "aqi": 29,
        "area": "成都",
        "pm2_5": 20,
        "pm2_5_24h": 26,
        "position_name": "十里店",
        "primary_pollutant": null,
        "quality": "优",
        "station_code": "1432A",
        "time_point": "2018-09-05T09:00:00Z"
    }
]
"""

JSON 数据中 key 必须是字符串类型,缺失值用 null 表示。其中还可能包含的基本类型有:字典,列表,字符串,数值,布尔值等。

Pandas 中的 read_json 往往能直接把 JSON 解析为 DataFrame。

pd.read_json(obj)

上面的结果是不是看起来非常舒服,DataFrame 的确是最佳的数据呈现格式。不过,由于 JSON 支持复杂的嵌套,有时候直接通过 read_json 读取到的 DataFrame 并不是我们想要的样子,例如某个键值是以字典或列表存在。此时,我们就会用其他的工具来解析 JSON 了。

Python 中有许多能够储存和解析 JSON 的库,这里推荐使用内建库 json。上面给出的 obj 其实是字符串,如果想要将其转换为 Python 的数据类型(Python Object),那么就可以使用 json.loads(obj) 完成。

import json

obj = json.loads(obj)
obj

现在,你就可以使用 Python 对字典、列表等操作方法来对上面的 obj 进行处理了。例如,我们想读取「金泉两河」地名。

obj[0]['position_name']

除此之外,使用 json.dumps 可以把 Python Object 转换为 JSON 类型。

json.dumps(obj)

read_ 操作参数详解

Pandas 中的 read_ 操作可不仅仅是加载数据文件这么简单,它带有的一些参数可以实现更加个性化的读取结果。我们以 pandas.read_csv() 的 API 为例。

pandas.read_csv(filepath_or_buffer, sep=', ', delimiter=None, header='infer', names=None, index_col=None, usecols=None, squeeze=False, prefix=None, mangle_dupe_cols=True, dtype=None, engine=None, converters=None, true_values=None, false_values=None, skipinitialspace=False, skiprows=None, nrows=None, na_values=None, keep_default_na=True, na_filter=True, verbose=False, skip_blank_lines=True, parse_dates=False, infer_datetime_format=False, keep_date_col=False, date_parser=None, dayfirst=False, iterator=False, chunksize=None, compression='infer', thousands=None, decimal=b'.', lineterminator=None, quotechar='"', quoting=0, escapechar=None, comment=None, encoding=None, dialect=None, tupleize_cols=None, error_bad_lines=True, warn_bad_lines=True, skipfooter=0, doublequote=True, delim_whitespace=False, low_memory=True, memory_map=False, float_precision=None)

上面的参数可能让你看到眼花缭乱,我们挑几个重要的介绍。

  • path:路径不仅仅可以读取本地文件,还支持远程 URL 链接。
  • sep:支持按特定字符分割。
  • header:可以指定某一行为列名,默认是第一行。
  • names:自定义列名。
  • skiprows:指定忽略某些行。
  • na_values:对空值进行指定替换操作。
  • parse_dates:尝试将数据解析为日期。
  • nrows:读取指定行数的数据。
  • chunksize:指定分块读取数据大小。
  • encoding:指定文件编码。

上面的这些参数使得 Pandas 的数据文件读取功能非常强大。例如,如果你的 CSV 文件是使用分号 ; 而不是逗号 , 分割,那么就可以通过 sep=';' 让数据加载为正常的 DataFrame 格式。

除此之外,像 skiprows 非常常用,它可以指定忽略某些行。,使得在加载数据时就可以对数据实现过滤,面对庞大且加载较慢的数据文件时特别好用。我们拿上面的示例数据举例。

pd.read_csv("test.csv", skiprows=range(1, 5))  # 忽略前 4 行的数据

分块读取数据

这里,我们特别介绍一下分块读取的方法。在很多时候,手中的数据集都非常大。例如当我们直接读取一个 GB 级别的 CSV 文件时,不仅速度很慢,还有可能因为内存不足而报错。此时,通过分块读取的方式加载数据文件就非常方便了。

通过上面的 read_ 参数可以看出,分块读取需要指定 chunksize,也就是每一块的大小。究竟该怎么做,我们来试一下。

chunker = pd.read_csv("test.csv", chunksize=2)
chunker

chunker 返回的 pandas.io.parsers.TextFileReader 是一个可迭代对象。你可以通过 get_chunk() 逐次返回每一个块状数据的内容。你可以尝试多次运行下方单元格,以查看每次迭代的结果。

chunker.get_chunk()  # 迭代返回分块数据内容

分块读取是解决大文件读取慢的有效手段,但需要注意 chunksize 并不是 Pandas 中每个 read_ 操作都支持的参数,这需要你在使用时通过官方文档确认。

另外,分块读取也不适宜用于解决读取部分数据的需求。例如,你需要读取某个数据的前 1 万条,应该直接使用切片,而非分块读取。当然,如果数据文件本身非常大,全部读取后切片会爆掉内存,此时才宜用分块读取进行解决。

小结

这篇文章中,我们重点介绍了实验 Pandas 读取和存储数据的方法。对常用的 Excel,CSV,HDF5,JSON 等数据格式做了介绍。同时,介绍了快速解析网页表格数据的方法。实验的最后,介绍了分块读取数据的方法,以便于读取较大的数据文件。

本篇文章需 特别授权许可,内容版权归作者所有,未经授权,禁止转载。

系列文章