Pandas 2.2 中文官方教程和指南(十·二)(1)https://developer.aliyun.com/article/1509786
处理索引
将 DataFrame 序列化为 parquet 文件可能会将隐式索引作为一个或多个列包含在输出文件中。因此,这段代码:
In [626]: df = pd.DataFrame({"a": [1, 2], "b": [3, 4]}) In [627]: df.to_parquet("test.parquet", engine="pyarrow")
如果您使用pyarrow进行序列化,将创建一个包含三列的 Parquet 文件:a、b和__index_level_0__。如果您使用fastparquet,索引可能会或可能不会写入文件。
这意外的额外列会导致一些数据库(如 Amazon Redshift)拒绝该文件,因为该列在目标表中不存在。
如果你想在写入时省略数据框的索引,请在to_parquet()中传递index=False:
In [628]: df.to_parquet("test.parquet", index=False)
当你将这个文件加载到DataFrame中时,这将创建一个只包含两个预期列a和b的 Parquet 文件。如果你的DataFrame有自定义索引,当你加载这个文件时将不会得到它。
传递index=True将始终写入索引,即使这不是底层引擎的默认行为。
对 Parquet 文件进行分区
Parquet 支持根据一个或多个列的值对数据进行分区。
In [629]: df = pd.DataFrame({"a": [0, 0, 1, 1], "b": [0, 1, 0, 1]}) In [630]: df.to_parquet(path="test", engine="pyarrow", partition_cols=["a"], compression=None)
path指定将数据保存到的父目录。partition_cols是数据集将根据其进行分区的列名。列按给定顺序进行分区。分区拆分由分区列中的唯一值确定。上面的示例创建了一个可能如下所示的分区数据集:
test ├── a=0 │ ├── 0bac803e32dc42ae83fddfd029cbdebc.parquet │ └── ... └── a=1 ├── e6ab24a4f45147b49b54a662f0c412a3.parquet └── ... ```## ORC 与 parquet 格式类似,[ORC 格式](https://orc.apache.org/)是用于数据框的二进制列序列化。它旨在使数据框的读取效率更高。pandas 为 ORC 格式提供了读取器和写入器,`read_orc()`和`to_orc()`。这需要[pyarrow](https://arrow.apache.org/docs/python/)库。 警告 + 建议使用 conda 安装 pyarrow,因为 pyarrow 存在一些问题。 + `to_orc()`需要 pyarrow>=7.0.0。 + `read_orc()`和`to_orc()`目前尚不支持 Windows,您可以在安装可选依赖项中找到有效的环境。 + 有关支持的数据类型,请参考[Arrow 中支持的 ORC 功能](https://arrow.apache.org/docs/cpp/orc.html#data-types)。 + 目前,将数据框转换为 ORC 文件时,日期时间列中的时区信息不会被保留。 ```py In [631]: df = pd.DataFrame( .....: { .....: "a": list("abc"), .....: "b": list(range(1, 4)), .....: "c": np.arange(4.0, 7.0, dtype="float64"), .....: "d": [True, False, True], .....: "e": pd.date_range("20130101", periods=3), .....: } .....: ) .....: In [632]: df Out[632]: a b c d e 0 a 1 4.0 True 2013-01-01 1 b 2 5.0 False 2013-01-02 2 c 3 6.0 True 2013-01-03 In [633]: df.dtypes Out[633]: a object b int64 c float64 d bool e datetime64[ns] dtype: object
写入 orc 文件。
In [634]: df.to_orc("example_pa.orc", engine="pyarrow")
从 orc 文件中读取。
In [635]: result = pd.read_orc("example_pa.orc") In [636]: result.dtypes Out[636]: a object b int64 c float64 d bool e datetime64[ns] dtype: object
仅读取 orc 文件的某些列。
In [637]: result = pd.read_orc( .....: "example_pa.orc", .....: columns=["a", "b"], .....: ) .....: In [638]: result.dtypes Out[638]: a object b int64 dtype: object ```## SQL 查询 `pandas.io.sql`模块提供了一组查询包装器,旨在促进数据检索并减少对特定于数据库的 API 的依赖。 如有可能,用户可能首选选择[Apache Arrow ADBC](https://arrow.apache.org/adbc/current/index.html)驱动程序。这些驱动程序应提供最佳性能、空值处理和类型检测。 > 版本 2.2.0 中的新功能:增加了对 ADBC 驱动程序的本机支持 有关 ADBC 驱动程序及其开发状态的完整列表,请参阅[ADBC 驱动程序实现状态](https://arrow.apache.org/adbc/current/driver/status.html)文档。 如果没有 ADBC 驱动程序或缺少功能,则用户应选择安装 SQLAlchemy 以及其数据库驱动程序库。这些驱动程序的示例是[psycopg2](https://www.psycopg.org/)用于 PostgreSQL 或[pymysql](https://github.com/PyMySQL/PyMySQL)用于 MySQL。对于[SQLite](https://docs.python.org/3/library/sqlite3.html),这在 Python 的标准库中默认包含。您可以在[SQLAlchemy 文档](https://docs.sqlalchemy.org/en/latest/dialects/index.html)中找到每个 SQL 方言支持的驱动程序的概述。 如果未安装 SQLAlchemy,可以使用[`sqlite3.Connection`](https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection "(在 Python v3.12 中)")代替 SQLAlchemy 引擎、连接或 URI 字符串。 还可以查看一些烹饪书示例以获取一些高级策略。 关键函数包括: | `read_sql_table`(table_name, con[, schema, ...]) | 将 SQL 数据库表读取到数据框中。 | | --- | --- | | `read_sql_query`(sql, con[, index_col, ...]) | 将 SQL 查询读取到数据框中。 | | `read_sql`(sql, con[, index_col, ...]) | 将 SQL 查询或数据库表读取到数据框中。 | | `DataFrame.to_sql`(name, con, *[, schema, ...]) | 将存储在数据框中的记录写入 SQL 数据库。 | 注意 函数`read_sql()`是对`read_sql_table()`和`read_sql_query()`(以及向后兼容性)的便捷包装,并根据提供的输入(数据库表名或 SQL 查询)委托给特定函数。如果表名包含特殊字符,则不需要对表名加引号。 在以下示例中,我们使用 [SQlite](https://www.sqlite.org/index.html) SQL 数据库引擎。您可以使用一个临时 SQLite 数据库,其中数据存储在“内存”中。 要使用 ADBC 驱动程序进行连接,您需要使用包管理器安装`adbc_driver_sqlite`。安装后,您可以使用 ADBC 驱动程序提供的 DBAPI 接口连接到数据库。 ```py import adbc_driver_sqlite.dbapi as sqlite_dbapi # Create the connection with sqlite_dbapi.connect("sqlite:///:memory:") as conn: df = pd.read_sql_table("data", conn)
要使用 SQLAlchemy 进行连接,您可以使用create_engine()函数从数据库 URI 创建一个引擎对象。您只需要为每个要连接的数据库创建一次引擎。有关create_engine()和 URI 格式化的更多信息,请参见下面的示例和 SQLAlchemy 文档
In [639]: from sqlalchemy import create_engine # Create your engine. In [640]: engine = create_engine("sqlite:///:memory:")
如果您想管理自己的连接,可以传递其中一个。下面的示例使用 Python 上下文管理器打开与数据库的连接,在块完成后自动关闭连接。请参阅SQLAlchemy 文档了解数据库连接是如何处理的。
with engine.connect() as conn, conn.begin(): data = pd.read_sql_table("data", conn)
警告
当你打开与数据库的连接时,你也有责任关闭它。保持连接打开的副作用可能包括锁定数据库或其他破坏性行为。
写入数据框
假设以下数据存储在一个DataFrame data中,我们可以使用to_sql()将其插入到数据库中。
| id | 日期 | 列 _1 | 列 _2 | 列 _3 |
| 26 | 2012-10-18 | X | 25.7 | True |
| 42 | 2012-10-19 | Y | -12.4 | False |
| 63 | 2012-10-20 | Z | 5.73 | True |
In [641]: import datetime In [642]: c = ["id", "Date", "Col_1", "Col_2", "Col_3"] In [643]: d = [ .....: (26, datetime.datetime(2010, 10, 18), "X", 27.5, True), .....: (42, datetime.datetime(2010, 10, 19), "Y", -12.5, False), .....: (63, datetime.datetime(2010, 10, 20), "Z", 5.73, True), .....: ] .....: In [644]: data = pd.DataFrame(d, columns=c) In [645]: data Out[645]: id Date Col_1 Col_2 Col_3 0 26 2010-10-18 X 27.50 True 1 42 2010-10-19 Y -12.50 False 2 63 2010-10-20 Z 5.73 True In [646]: data.to_sql("data", con=engine) Out[646]: 3
在某些数据库中,写入大型 DataFrame 可能会因超出数据包大小限制而导致错误。可以通过在调用 to_sql 时设置 chunksize 参数来避免这种情况。例如,以下代码将以每次 1000 行的批量方式将 data 写入数据库:
In [647]: data.to_sql("data_chunked", con=engine, chunksize=1000) Out[647]: 3
SQL 数据类型
确保跨 SQL 数据库的一致数据类型管理具有挑战性。并非每个 SQL 数据库都提供相同的类型,即使提供了,给定类型的实现方式也可能有微妙的差异,对类型的保留方式可能产生细微影响。
为了最大限度地保留数据库类型,建议用户在可用时使用 ADBC 驱动程序。Arrow 类型系统提供了更广泛的类型数组,与历史上的 pandas/NumPy 类型系统更接近匹配数据库类型。举例来说,注意一下不同数据库和 pandas 后端中可用的类型(非穷尽列表):
| numpy/pandas | arrow | postgres | sqlite |
| int16/Int16 | int16 | SMALLINT | INTEGER |
| int32/Int32 | int32 | INTEGER | INTEGER |
| int64/Int64 | int64 | BIGINT | INTEGER |
| float32 | float32 | REAL | REAL |
| float64 | float64 | DOUBLE PRECISION | REAL |
| object | string | TEXT | TEXT |
| bool | bool_ |
BOOLEAN | |
| datetime64[ns] | timestamp(us) | TIMESTAMP | |
| datetime64[ns,tz] | timestamp(us,tz) | TIMESTAMPTZ | |
| date32 | DATE | ||
| month_day_nano_interval | INTERVAL | ||
| binary | BINARY | BLOB | |
| decimal128 | DECIMAL [1] | ||
| list | ARRAY [1] | ||
| struct |
复合类型
脚注
如果您希望在 DataFrame 的整个生命周期中尽可能保留数据库类型,建议用户利用 read_sql() 的 dtype_backend="pyarrow" 参数。
# for roundtripping with pg_dbapi.connect(uri) as conn: df2 = pd.read_sql("pandas_table", conn, dtype_backend="pyarrow")
这将防止您的数据被转换为传统的 pandas/NumPy 类型系统,后者经常以使 SQL 类型无法往返的方式进行转换。
如果没有可用的 ADBC 驱动程序,to_sql() 将尝试根据数据的 dtype 将数据映射到适当的 SQL 数据类型。当您有 dtype 为 object 的列时,pandas 将尝试推断数据类型。
您可以通过使用 dtype 参数指定任何列的所需 SQL 类型来始终覆盖默认类型。该参数需要一个将列名映射到 SQLAlchemy 类型(或字符串以用于 sqlite3 回退模式)的字典。例如,指定为字符串列使用 sqlalchemy 的 String 类型而不是默认的 Text 类型:
In [648]: from sqlalchemy.types import String In [649]: data.to_sql("data_dtype", con=engine, dtype={"Col_1": String}) Out[649]: 3
注意
由于不同数据库版本对 timedelta 的支持有限,类型为timedelta64的列将被写入为纳秒整数值到数据库中,并会引发警告。唯一的例外是在使用 ADBC PostgreSQL 驱动程序时,此时 timedelta 将被写入数据库作为INTERVAL。
注意
category dtype 的列将被转换为密集表示,就像使用np.asarray(categorical)一样(例如,对于字符串类别,这将生成一个字符串数组)。因此,将数据库表重新读取时不会生成分类数据。
日期时间数据类型
使用 ADBC 或 SQLAlchemy,to_sql() 能够写入时区无关或时区感知的日期时间数据。然而,最终存储在数据库中的数据取决于所使用的数据库系统支持的日期时间数据类型。
下表列出了一些常见数据库支持的日期时间数据类型。其他数据库方言可能有不同的日期时间数据类型。
| 数据库 | SQL 日期时间类型 | 时区支持 |
| SQLite | TEXT |
否 |
| MySQL | TIMESTAMP 或 DATETIME |
否 |
| PostgreSQL | TIMESTAMP 或 TIMESTAMP WITH TIME ZONE |
是 |
当将带有时区信息的数据写入不支持时区的数据库时,数据将被写入为相对于时区的本地时间的时区无关时间戳。
read_sql_table() 也能够读取时区感知或时区无关的日期时间数据。当读取TIMESTAMP WITH TIME ZONE类型时,pandas 将数据转换为 UTC 时间。
插入方法
参数method控制所使用的 SQL 插入子句。可能的值包括:
None:使用标准 SQLINSERT子句(每行一个)。'multi':在单个INSERT子句中传递多个值。它使用一种特殊的 SQL 语法,不是所有后端都支持。这通常对于像Presto和Redshift这样的分析数据库提供更好的性能,但如果表包含许多列,则传统 SQL 后端的性能会更差。有关更多信息,请查看 SQLAlchemy 的文档。- 具有签名
(pd_table, conn, keys, data_iter)的可调用函数:这可用于基于特定后端方言功能实现更高性能的插入方法。
使用 PostgreSQL 的示例可调用复制子句:
# Alternative to_sql() *method* for DBs that support COPY FROM import csv from io import StringIO def psql_insert_copy(table, conn, keys, data_iter): """ Execute SQL statement inserting data Parameters ---------- table : pandas.io.sql.SQLTable conn : sqlalchemy.engine.Engine or sqlalchemy.engine.Connection keys : list of str Column names data_iter : Iterable that iterates the values to be inserted """ # gets a DBAPI connection that can provide a cursor dbapi_conn = conn.connection with dbapi_conn.cursor() as cur: s_buf = StringIO() writer = csv.writer(s_buf) writer.writerows(data_iter) s_buf.seek(0) columns = ', '.join(['"{}"'.format(k) for k in keys]) if table.schema: table_name = '{}.{}'.format(table.schema, table.name) else: table_name = table.name sql = 'COPY {} ({}) FROM STDIN WITH CSV'.format( table_name, columns) cur.copy_expert(sql=sql, file=s_buf)
读取表
read_sql_table() 将读取给定表名的数据库表,可选择性地读取一部分列。
注意
为了使用read_sql_table(),你必须安装 ADBC 驱动程序或 SQLAlchemy 可选依赖项。
In [650]: pd.read_sql_table("data", engine) Out[650]: index id Date Col_1 Col_2 Col_3 0 0 26 2010-10-18 X 27.50 True 1 1 42 2010-10-19 Y -12.50 False 2 2 63 2010-10-20 Z 5.73 True
注意
ADBC 驱动程序将数据库类型直接映射回 arrow 类型。对于其他驱动程序,请注意 pandas 从查询输出中推断列 dtype,而不是通过查找物理数据库模式中的数据类型。例如,假设userid是表中的整数列。那么,直观地,select userid ...将返回整数值系列,而select cast(userid as text) ...将返回对象值(str)系列。因此,如果查询输出为空,则所有生成的列将作为对象值返回(因为它们是最一般的)。如果你预见到你的查询有时会生成��结果,你可能希望在之后明确进行类型转换以确保 dtype 的完整性。
你还可以指定列的名称作为DataFrame索引,并指定要读取的列的子集。
In [651]: pd.read_sql_table("data", engine, index_col="id") Out[651]: index Date Col_1 Col_2 Col_3 id 26 0 2010-10-18 X 27.50 True 42 1 2010-10-19 Y -12.50 False 63 2 2010-10-20 Z 5.73 True In [652]: pd.read_sql_table("data", engine, columns=["Col_1", "Col_2"]) Out[652]: Col_1 Col_2 0 X 27.50 1 Y -12.50 2 Z 5.73
你还可以明确强制将列解析为日期:
In [653]: pd.read_sql_table("data", engine, parse_dates=["Date"]) Out[653]: index id Date Col_1 Col_2 Col_3 0 0 26 2010-10-18 X 27.50 True 1 1 42 2010-10-19 Y -12.50 False 2 2 63 2010-10-20 Z 5.73 True
如果需要,你可以明确指定格式字符串,或传递给pandas.to_datetime()的参数字典:
pd.read_sql_table("data", engine, parse_dates={"Date": "%Y-%m-%d"}) pd.read_sql_table( "data", engine, parse_dates={"Date": {"format": "%Y-%m-%d %H:%M:%S"}}, )
使用has_table()可以检查表是否存在
模式支持
通过read_sql_table()和to_sql()函数中的schema关键字支持从不同模式读取和写入。但请注意,这取决于数据库类型(sqlite 没有模式)。例如:
df.to_sql(name="table", con=engine, schema="other_schema") pd.read_sql_table("table", engine, schema="other_schema")
查询
你可以在read_sql_query()函数中使用原始 SQL 进行查询。在这种情况下,你必须使用适合你的数据库的 SQL 变体。当使用 SQLAlchemy 时,你还可以传递数据库无关的 SQLAlchemy 表达式语言构造。
In [654]: pd.read_sql_query("SELECT * FROM data", engine) Out[654]: index id Date Col_1 Col_2 Col_3 0 0 26 2010-10-18 00:00:00.000000 X 27.50 1 1 1 42 2010-10-19 00:00:00.000000 Y -12.50 0 2 2 63 2010-10-20 00:00:00.000000 Z 5.73 1
当然,你可以指定一个更“复杂”的查询。
In [655]: pd.read_sql_query("SELECT id, Col_1, Col_2 FROM data WHERE id = 42;", engine) Out[655]: id Col_1 Col_2 0 42 Y -12.5
read_sql_query()函数支持chunksize参数。指定这个参数将返回查询结果的迭代器:
In [656]: df = pd.DataFrame(np.random.randn(20, 3), columns=list("abc")) In [657]: df.to_sql(name="data_chunks", con=engine, index=False) Out[657]: 20
In [658]: for chunk in pd.read_sql_query("SELECT * FROM data_chunks", engine, chunksize=5): .....: print(chunk) .....: a b c 0 -0.395347 -0.822726 -0.363777 1 1.676124 -0.908102 -1.391346 2 -1.094269 0.278380 1.205899 3 1.503443 0.932171 -0.709459 4 -0.645944 -1.351389 0.132023 a b c 0 0.210427 0.192202 0.661949 1 1.690629 -1.046044 0.618697 2 -0.013863 1.314289 1.951611 3 -1.485026 0.304662 1.194757 4 -0.446717 0.528496 -0.657575 a b c 0 -0.876654 0.336252 0.172668 1 0.337684 -0.411202 -0.828394 2 -0.244413 1.094948 0.087183 3 1.125934 -1.480095 1.205944 4 -0.451849 0.452214 -2.208192 a b c 0 -2.061019 0.044184 -0.017118 1 1.248959 -0.675595 -1.908296 2 -0.125934 1.491974 0.648726 3 0.391214 0.438609 1.634248 4 1.208707 -1.535740 1.620399
引擎连接示例
要使用 SQLAlchemy 连接,你可以使用create_engine()函数从数据库 URI 创建一个引擎对象。你只需要为每个要连接的数据库创建一次引擎。
from sqlalchemy import create_engine engine = create_engine("postgresql://scott:tiger@localhost:5432/mydatabase") engine = create_engine("mysql+mysqldb://scott:tiger@localhost/foo") engine = create_engine("oracle://scott:[[email protected]](/cdn-cgi/l/email-protection):1521/sidname") engine = create_engine("mssql+pyodbc://mydsn") # sqlite://<nohostname>/<path> # where <path> is relative: engine = create_engine("sqlite:///foo.db") # or absolute, starting with a slash: engine = create_engine("sqlite:absolute/path/to/foo.db")
欲了解更多信息,请参阅 SQLAlchemy 文档中的示例文档
高级 SQLAlchemy 查询
你可以使用 SQLAlchemy 构造描述你的查询。
使用sqlalchemy.text()以与后端无关的方式指定查询参数
In [659]: import sqlalchemy as sa In [660]: pd.read_sql( .....: sa.text("SELECT * FROM data where Col_1=:col1"), engine, params={"col1": "X"} .....: ) .....: Out[660]: index id Date Col_1 Col_2 Col_3 0 0 26 2010-10-18 00:00:00.000000 X 27.5 1
如果您有数据库的 SQLAlchemy 描述,可以使用 SQLAlchemy 表达式表示 where 条件
In [661]: metadata = sa.MetaData() In [662]: data_table = sa.Table( .....: "data", .....: metadata, .....: sa.Column("index", sa.Integer), .....: sa.Column("Date", sa.DateTime), .....: sa.Column("Col_1", sa.String), .....: sa.Column("Col_2", sa.Float), .....: sa.Column("Col_3", sa.Boolean), .....: ) .....: In [663]: pd.read_sql(sa.select(data_table).where(data_table.c.Col_3 is True), engine) Out[663]: Empty DataFrame Columns: [index, Date, Col_1, Col_2, Col_3] Index: []
您可以将 SQLAlchemy 表达式与传递给 read_sql() 的参数结合使用 sqlalchemy.bindparam()
In [664]: import datetime as dt In [665]: expr = sa.select(data_table).where(data_table.c.Date > sa.bindparam("date")) In [666]: pd.read_sql(expr, engine, params={"date": dt.datetime(2010, 10, 18)}) Out[666]: index Date Col_1 Col_2 Col_3 0 1 2010-10-19 Y -12.50 False 1 2 2010-10-20 Z 5.73 True
Sqlite 回退
支持使用 sqlite 而不使用 SQLAlchemy。此模式需要一个遵守 Python DB-API 的 Python 数据库适配器。
您可以这样创建连接:
import sqlite3 con = sqlite3.connect(":memory:")
然后发出以下查询:
data.to_sql("data", con) pd.read_sql_query("SELECT * FROM data", con) ```## Google BigQuery `pandas-gbq` 包提供了与 Google BigQuery 读写的功能。 pandas 与这个外部包集成。如果安装了 `pandas-gbq`,则可以使用 pandas 方法 `pd.read_gbq` 和 `DataFrame.to_gbq`,这将调用 `pandas-gbq` 中的相应函数。 完整文档可以在[这里](https://pandas-gbq.readthedocs.io/en/latest/)找到。 ## Stata 格式 ### 写入到 Stata 格式 方法 `DataFrame.to_stata()` 将 DataFrame 写入 .dta 文件。此文件的格式版本始终为 115(Stata 12)。 ```py In [667]: df = pd.DataFrame(np.random.randn(10, 2), columns=list("AB")) In [668]: df.to_stata("stata.dta")
Stata 数据文件具有有限的数据类型支持;只能在 .dta 文件中存储包含 244 个或更少字符的字符串,int8, int16, int32, float32 和 float64。此外,Stata 保留某些值来表示缺失数据。导出特定数据类型的非缺失值超出 Stata 允许范围的值将重新定义变量为下一个更大的大小。例如,在 Stata 中,int8 值限制在 -127 和 100 之间,因此值大于 100 的变量将触发转换为 int16。浮点数据类型中的 nan 值存储为基本缺失数据类型(Stata 中的 .)。
注意
无法导出整数数据类型的缺失数据值。
Stata 写入器优雅地处理其他数据类型,包括 int64, bool, uint8, uint16, uint32,通过将其转换为可以表示数据的最小支持类型。例如,类型为 uint8 的数据将被转换为 int8,如果所有值都小于 100(Stata 中非缺失 int8 数据的上限),或者,如果值超出此范围,则变量将被转换为 int16。
警告
从 int64 转换为 float64 可能会导致精度损失,如果 int64 值大于 2**53。
警告
StataWriter 和 DataFrame.to_stata() 仅支持包含最多 244 个字符的固定宽度字符串,这是版本 115 dta 文件格式所施加的限制。尝试写入长度超过 244 个字符的字符串的 Stata dta 文件会引发 ValueError。 ### 从 Stata 格式读取
顶层函数 read_stata 将读取一个 dta 文件,并返回一个 DataFrame 或一个 pandas.api.typing.StataReader,可用于逐步读取文件。
In [669]: pd.read_stata("stata.dta") Out[669]: index A B 0 0 -0.165614 0.490482 1 1 -0.637829 0.067091 2 2 -0.242577 1.348038 3 3 0.647699 -0.644937 4 4 0.625771 0.918376 5 5 0.401781 -1.488919 6 6 -0.981845 -0.046882 7 7 -0.306796 0.877025 8 8 -0.336606 0.624747 9 9 -1.582600 0.806340
指定chunksize会产生一个pandas.api.typing.StataReader实例,可以用来一次从文件中读取chunksize行。StataReader对象可以用作迭代器。
In [670]: with pd.read_stata("stata.dta", chunksize=3) as reader: .....: for df in reader: .....: print(df.shape) .....: (3, 3) (3, 3) (3, 3) (1, 3)
要获得更精细的控制,请使用iterator=True并在每次调用read()时指定chunksize。
In [671]: with pd.read_stata("stata.dta", iterator=True) as reader: .....: chunk1 = reader.read(5) .....: chunk2 = reader.read(5) .....:
目前index被检索为一列。
参数convert_categoricals指示是否应读取值标签并使用它们创建Categorical变量。值标签也可以通过函数value_labels检索,但在使用之前需要调用read()。
参数convert_missing指示是否应保留 Stata 中的缺失值表示。如果为False(默认值),缺失值将表示为np.nan。如果为True,缺失值将使用StataMissingValue对象表示,并且包含缺失值的列将具有object数据类型。
注意
read_stata()和StataReader支持.dta 格式 113-115(Stata 10-12)、117(Stata 13)和 118(Stata 14)。
注意
��置preserve_dtypes=False将升级为标准的 pandas 数据类型:所有整数类型为int64,浮点数据为float64。默认情况下,导入时保留 Stata 数据类型。
注意
所有StataReader对象,无论是由read_stata()(使用iterator=True或chunksize)创建还是手动实例化,都必须作为上下文管理器使用(例如with语句)。虽然close()方法可用,但不受支持。它不是公共 API 的一部分,并将在未来的某个时候被删除而没有警告。
分类数据
分类数据可以导出为Stata数据文件,作为带有值标签的数据。导出的数据包括底层类别代码作为整数数据值和类别作为值标签。在导出时,Stata没有明确的等价Categorical,并且关于变量是否有序的信息会丢失。
警告
Stata仅支持字符串值标签,因此在导出数据时会调用str。使用非字符串类别导出Categorical变量会产生警告,并且如果类别的str表示不唯一,则可能导致信息丢失。
类似地,可以使用关键字参数convert_categoricals(默认为True)从Stata数据文件中导入带有值标签的Categorical变量。关键字参数order_categoricals(默认为True)确定导入的Categorical变量是否有序。
注意
在导入分类数据时,Stata 数据文件中的变量值不会被保留,因为Categorical变量始终使用介于-1和n-1之间的整数数据类型,其中n是类别数。如果需要原始值,可以通过设置convert_categoricals=False来导入原始数据(但不包括变量标签)。原始值可以与导入的分类数据匹配,因为原始Stata数据值与导入的Categorical变量的类别代码之间存在简单的映射:缺失值被分配代码-1,最小的原始值被分配0,第二小的被分配1,依此类推,直到最大的原始值被分配代码n-1。
注意
Stata 支持部分标记的系列。这些系列对一些数据值有值标签,但并非所有数据值都有。导入部分标记的系列将产生一个具有字符串类别的Categorical,对于已标记的值和没有标记的值,将产生数值类别。 ## SAS 格式
顶层函数read_sas()可以读取(但不能写入)SAS XPORT(.xpt)和 SAS7BDAT(.sas7bdat)格式文件。
SAS 文件只包含两种值类型:ASCII 文本和浮点值(通常为 8 字节,但有时被截断)。对于 xport 文件,没有自动将类型转换为整数、日期或分类变量。对于 SAS7BDAT 文件,格式代码可能允许日期变量自动转换为日期。默认情况下,整个文件被读取并返回为DataFrame。
指定chunksize或使用iterator=True以获取读取器对象(XportReader或SAS7BDATReader),以逐步读取文件。读取器对象还具有包含有关文件及其变量的其他信息的属性。
读取一个 SAS7BDAT 文件:
df = pd.read_sas("sas_data.sas7bdat")
获取一个迭代器,并每次读取一个 XPORT 文件的 100,000 行:
def do_something(chunk): pass with pd.read_sas("sas_xport.xpt", chunk=100000) as rdr: for chunk in rdr: do_something(chunk)
可从 SAS 网站获取 xport 文件格式的规范。
没有关于 SAS7BDAT 格式的官方文档。 ## SPSS 格式
顶层函数read_spss()可以读取(但不能写入)SPSS SAV(.sav)和 ZSAV(.zsav)格式文件。
SPSS 文件包含列名。默认情况下,整个文件被读取,分类列被转换为pd.Categorical,并返回一个包含所有列的DataFrame。
指定usecols参数以获取列的子集。指定convert_categoricals=False以避免将分类列转换为pd.Categorical。
读取一个 SPSS 文件:
df = pd.read_spss("spss_data.sav")
从 SPSS 文件中提取usecols中包含的列的子集,并避免将分类列转换为pd.Categorical:
df = pd.read_spss( "spss_data.sav", usecols=["foo", "bar"], convert_categoricals=False, )
有关 SAV 和 ZSAV 文件格式的更多信息,请参阅此处。 ## 其他文件格式
pandas 本身仅支持与其表格数据模型清晰映射的有限一组文件格式的 IO。为了将其他文件格式读取和写入 pandas,我们建议使用来自更广泛社区的这些软件包。
netCDF
xarray提供了受到 pandas DataFrame启发的数据结构,用于处理多维数据集,重点放在 netCDF 文件格式上,并且易于与 pandas 之间进行转换。 ## 性能考虑
这是对各种 IO 方法的非正式比较,使用 pandas 0.24.2。时间取决于机器,应忽略小差异。
In [1]: sz = 1000000 In [2]: df = pd.DataFrame({'A': np.random.randn(sz), 'B': [1] * sz}) In [3]: df.info() <class 'pandas.core.frame.DataFrame'> RangeIndex: 1000000 entries, 0 to 999999 Data columns (total 2 columns): A 1000000 non-null float64 B 1000000 non-null int64 dtypes: float64(1), int64(1) memory usage: 15.3 MB
下面将使用以下测试函数来比较几种 IO 方法的性能:
import numpy as np import os sz = 1000000 df = pd.DataFrame({"A": np.random.randn(sz), "B": [1] * sz}) sz = 1000000 np.random.seed(42) df = pd.DataFrame({"A": np.random.randn(sz), "B": [1] * sz}) def test_sql_write(df): if os.path.exists("test.sql"): os.remove("test.sql") sql_db = sqlite3.connect("test.sql") df.to_sql(name="test_table", con=sql_db) sql_db.close() def test_sql_read(): sql_db = sqlite3.connect("test.sql") pd.read_sql_query("select * from test_table", sql_db) sql_db.close() def test_hdf_fixed_write(df): df.to_hdf("test_fixed.hdf", key="test", mode="w") def test_hdf_fixed_read(): pd.read_hdf("test_fixed.hdf", "test") def test_hdf_fixed_write_compress(df): df.to_hdf("test_fixed_compress.hdf", key="test", mode="w", complib="blosc") def test_hdf_fixed_read_compress(): pd.read_hdf("test_fixed_compress.hdf", "test") def test_hdf_table_write(df): df.to_hdf("test_table.hdf", key="test", mode="w", format="table") def test_hdf_table_read(): pd.read_hdf("test_table.hdf", "test") def test_hdf_table_write_compress(df): df.to_hdf( "test_table_compress.hdf", key="test", mode="w", complib="blosc", format="table" ) def test_hdf_table_read_compress(): pd.read_hdf("test_table_compress.hdf", "test") def test_csv_write(df): df.to_csv("test.csv", mode="w") def test_csv_read(): pd.read_csv("test.csv", index_col=0) def test_feather_write(df): df.to_feather("test.feather") def test_feather_read(): pd.read_feather("test.feather") def test_pickle_write(df): df.to_pickle("test.pkl") def test_pickle_read(): pd.read_pickle("test.pkl") def test_pickle_write_compress(df): df.to_pickle("test.pkl.compress", compression="xz") def test_pickle_read_compress(): pd.read_pickle("test.pkl.compress", compression="xz") def test_parquet_write(df): df.to_parquet("test.parquet") def test_parquet_read(): pd.read_parquet("test.parquet")
在写入时,速度最快的三个函数是test_feather_write,test_hdf_fixed_write和test_hdf_fixed_write_compress。
In [4]: %timeit test_sql_write(df) 3.29 s ± 43.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) In [5]: %timeit test_hdf_fixed_write(df) 19.4 ms ± 560 µs per loop (mean ± std. dev. of 7 runs, 1 loop each) In [6]: %timeit test_hdf_fixed_write_compress(df) 19.6 ms ± 308 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) In [7]: %timeit test_hdf_table_write(df) 449 ms ± 5.61 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) In [8]: %timeit test_hdf_table_write_compress(df) 448 ms ± 11.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) In [9]: %timeit test_csv_write(df) 3.66 s ± 26.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) In [10]: %timeit test_feather_write(df) 9.75 ms ± 117 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) In [11]: %timeit test_pickle_write(df) 30.1 ms ± 229 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) In [12]: %timeit test_pickle_write_compress(df) 4.29 s ± 15.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) In [13]: %timeit test_parquet_write(df) 67.6 ms ± 706 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
在读取时,速度最快的三个函数是test_feather_read,test_pickle_read和test_hdf_fixed_read。
In [14]: %timeit test_sql_read() 1.77 s ± 17.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) In [15]: %timeit test_hdf_fixed_read() 19.4 ms ± 436 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) In [16]: %timeit test_hdf_fixed_read_compress() 19.5 ms ± 222 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) In [17]: %timeit test_hdf_table_read() 38.6 ms ± 857 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) In [18]: %timeit test_hdf_table_read_compress() 38.8 ms ± 1.49 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) In [19]: %timeit test_csv_read() 452 ms ± 9.04 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) In [20]: %timeit test_feather_read() 12.4 ms ± 99.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) In [21]: %timeit test_pickle_read() 18.4 ms ± 191 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) In [22]: %timeit test_pickle_read_compress() 915 ms ± 7.48 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) In [23]: %timeit test_parquet_read() 24.4 ms ± 146 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
文件test.pkl.compress,test.parquet和test.feather在磁盘上占用的空间最少(以字节为单位)。
29519500 Oct 10 06:45 test.csv 16000248 Oct 10 06:45 test.feather 8281983 Oct 10 06:49 test.parquet 16000857 Oct 10 06:47 test.pkl 7552144 Oct 10 06:48 test.pkl.compress 34816000 Oct 10 06:42 test.sql 24009288 Oct 10 06:43 test_fixed.hdf 24009288 Oct 10 06:43 test_fixed_compress.hdf 24458940 Oct 10 06:44 test_table.hdf 24458940 Oct 10 06:44 test_table_compress.hdf ```## CSV 和文本文件 用于读取文本文件(也称为平面文件)的主要函数是`read_csv()`。查看 cookbook 以获取一些高级策略。 ### 解析选项 `read_csv()`接受以下常见参数: #### 基本 filepath_or_buffervarious 要么是文件路径([`str`](https://docs.python.org/3/library/stdtypes.html#str "(在 Python v3.12 中)")),[`pathlib.Path`](https://docs.python.org/3/library/pathlib.html#pathlib.Path "(在 Python v3.12 中)"),或`py:py._path.local.LocalPath`),URL(包括 http,ftp 和 S3 位置),或具有`read()`方法的任何对象(例如打开的文件或[`StringIO`](https://docs.python.org/3/library/io.html#io.StringIO "(在 Python v3.12 中)"))。 sepstr,默认为`','`用于`read_csv()`,`\t`用于`read_table()` 要使用的分隔符。如果 sep 为`None`,C 引擎无法自动检测分隔符,但 Python 解析引擎可以,这意味着将使用后者,并通过 Python 的内置 sniffer 工具[`csv.Sniffer`](https://docs.python.org/3/library/csv.html#csv.Sniffer "(in Python v3.12)")自动检测分隔符。此外,长于 1 个字符且不同于`'\s+'`的分隔符将被解释为正则表达式,并且还会强制使用 Python 解析引擎。请注意,正则表达式分隔符容易忽略带引号的数据。正则表达式示例:`'\\r\\t'`。 分隔符字符串,默认为`None` sep 的替代参数名称。 delim_whitespace 布尔值,默认为 False 指定是否使用空格(例如`' '`或`'\t'`)作为分隔符。等效于设置`sep='\s+'`。如果将此选项设置为`True`,则不应为`delimiter`参数传递任何内容。 #### 列和索引位置及名称 headerint 或 int 列表,默认为`'infer'` 用作列名和数据起始位置的行号。默认行为是推断列名:如果没有传递名称,则行为与`header=0`相同,并且列名从文件的第一行推断出来,如果显式传递了列名,则行为与`header=None`相同。显式传递`header=0`以能够替换现有名称。 表头可以是指定列的 MultiIndex 的行位置列表,例如`[0,1,3]`。未指定的中间行将被跳过(例如,在此示例中跳过了 2)。请注意,如果`skip_blank_lines=True`,此参数将忽略注释行和空行,因此`header=0`表示数据的第一行而不是文件的第一行。 names 数组样式,默认为`None` 要使用的列名列表。如果文件不包含表头行,则应明确传递`header=None`。不允许在此列表中存在重复项。 index_colint、str、int/str 序列或 False,可选,默认为`None` 用作`DataFrame`行标签的列,可以是字符串名称或列索引。如果给定 int/str 序列,则使用 MultiIndex。 注意 `index_col=False`可用于强制 pandas*不*将第一列用作索引,例如当您有一个每行末尾都有分隔符的格式不正确的文件时。 `None`的默认值指示 pandas 进行猜测。如果列头行中的字段数等于数据文件主体中的字段数,则使用默认索引。如果大于,则使用前几列作为索引,以使数据主体中的字段数等于列头中的字段数。 表头后的第一行用于确定列数,这些列将进入索引。如果后续行的列数少于第一行,则用`NaN`填充。 可通过 `usecols` 避免这种情况。这确保列按原样采取,并且尾随数据被忽略。 usecols 类似列表或可调用函数,默认为 `None`。 返回列的子集。如果类似列表,所有元素必须是位置的(即整数索引到文档列)或与用户在 `names` 中提供的列名对应的字符串,或从文档标题行中推断出的列名。如果给定了 `names`,则不考虑文档标题行。例如,一个有效的类似列表 `usecols` 参数可以是 `[0, 1, 2]` 或 `['foo', 'bar', 'baz']`。 元素顺序被忽略,因此 `usecols=[0, 1]` 与 `[1, 0]` 相同。要从保留元素顺序的 `data` 实例化 DataFrame,请使用 `pd.read_csv(data, usecols=['foo', 'bar'])[['foo', 'bar']]` 以 `['foo', 'bar']` 顺序的列或 `pd.read_csv(data, usecols=['foo', 'bar'])[['bar', 'foo']]` 以 `['bar', 'foo']` 顺序。 如果可调用,则将对列名评估可调用函数,返回可调用函数评估为 True 的列名: ```py In [1]: import pandas as pd In [2]: from io import StringIO In [3]: data = "col1,col2,col3\na,b,1\na,b,2\nc,d,3" In [4]: pd.read_csv(StringIO(data)) Out[4]: col1 col2 col3 0 a b 1 1 a b 2 2 c d 3 In [5]: pd.read_csv(StringIO(data), usecols=lambda x: x.upper() in ["COL1", "COL3"]) Out[5]: col1 col3 0 a 1 1 a 2 2 c 3
使用此参数可在使用 c 引擎时获得更快的解析时间和更低的内存使用。Python 引擎在决定要删除哪些列之前会先加载数据。
Pandas 2.2 中文官方教程和指南(十·二)(3)https://developer.aliyun.com/article/1509788