数据类型和结构
数据类型
Polars 支持多种数据类型,大致分为以下几类
- 数值数据类型:有符号整数、无符号整数、浮点数和 Decimal 数。
- 嵌套数据类型:列表、结构体和数组。
- 时间类型:日期、日期时间、时间和时间差。
- 杂项:字符串、二进制数据、布尔值、分类数据、枚举和对象。
所有类型都支持由特殊值 null
表示的缺失值。这不应与浮点数数据类型中的特殊值 NaN
混淆;有关更多信息,请参阅浮点数部分。
您还可以在附录中找到包含所有支持的数据类型的完整表格,其中包含关于何时使用每种数据类型的说明以及指向文档相关部分的链接。
Series
Polars 提供的核心基础数据结构是 Series 和 Dataframe。Series 是一种一维同构数据结构。“同构”意味着 Series 中的所有元素都具有相同的数据类型。下面的代码片段展示了如何创建一个命名 Series。
shape: (5,)
Series: 'ints' [i64]
[
1
2
3
4
5
]
创建 Series 时,Polars 会从您提供的值推断数据类型。您可以指定具体的数据类型来覆盖推断机制。
s1 = pl.Series("ints", [1, 2, 3, 4, 5])
s2 = pl.Series("uints", [1, 2, 3, 4, 5], dtype=pl.UInt64)
print(s1.dtype, s2.dtype)
let s1 = Series::new("ints".into(), &[1, 2, 3, 4, 5]);
let s2 = Series::new("uints".into(), &[1, 2, 3, 4, 5])
.cast(&DataType::UInt64) // Here, we actually cast after inference.
.unwrap();
println!("{} {}", s1.dtype(), s2.dtype()); // i32 u64
Int64 UInt64
Dataframe
Dataframe 是一种二维异构数据结构,包含唯一命名的 Series。通过将数据存储在 Dataframe 中,您将能够使用 Polars API 编写查询来操作数据。您可以通过使用我们接下来将讨论的 Polars 提供的上下文和表达式来实现这一点。
下面的代码片段展示了如何从列表字典创建 Dataframe
from datetime import date
df = pl.DataFrame(
{
"name": ["Alice Archer", "Ben Brown", "Chloe Cooper", "Daniel Donovan"],
"birthdate": [
date(1997, 1, 10),
date(1985, 2, 15),
date(1983, 3, 22),
date(1981, 4, 30),
],
"weight": [57.9, 72.5, 53.6, 83.1], # (kg)
"height": [1.56, 1.77, 1.65, 1.75], # (m)
}
)
print(df)
use chrono::prelude::*;
let df: DataFrame = df!(
"name" => ["Alice Archer", "Ben Brown", "Chloe Cooper", "Daniel Donovan"],
"birthdate" => [
NaiveDate::from_ymd_opt(1997, 1, 10).unwrap(),
NaiveDate::from_ymd_opt(1985, 2, 15).unwrap(),
NaiveDate::from_ymd_opt(1983, 3, 22).unwrap(),
NaiveDate::from_ymd_opt(1981, 4, 30).unwrap(),
],
"weight" => [57.9, 72.5, 53.6, 83.1], // (kg)
"height" => [1.56, 1.77, 1.65, 1.75], // (m)
)
.unwrap();
println!("{df}");
shape: (4, 4)
┌────────────────┬────────────┬────────┬────────┐
│ name ┆ birthdate ┆ weight ┆ height │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ date ┆ f64 ┆ f64 │
╞════════════════╪════════════╪════════╪════════╡
│ Alice Archer ┆ 1997-01-10 ┆ 57.9 ┆ 1.56 │
│ Ben Brown ┆ 1985-02-15 ┆ 72.5 ┆ 1.77 │
│ Chloe Cooper ┆ 1983-03-22 ┆ 53.6 ┆ 1.65 │
│ Daniel Donovan ┆ 1981-04-30 ┆ 83.1 ┆ 1.75 │
└────────────────┴────────────┴────────┴────────┘
检查 Dataframe
在本小节中,我们将展示一些有用的方法来快速检查 Dataframe。我们将使用之前创建的 Dataframe 作为起点。
Head
函数 head
显示 Dataframe 的前几行。默认情况下,您会获得前 5 行,但您也可以指定所需的行数。
shape: (3, 4)
┌──────────────┬────────────┬────────┬────────┐
│ name ┆ birthdate ┆ weight ┆ height │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ date ┆ f64 ┆ f64 │
╞══════════════╪════════════╪════════╪════════╡
│ Alice Archer ┆ 1997-01-10 ┆ 57.9 ┆ 1.56 │
│ Ben Brown ┆ 1985-02-15 ┆ 72.5 ┆ 1.77 │
│ Chloe Cooper ┆ 1983-03-22 ┆ 53.6 ┆ 1.65 │
└──────────────┴────────────┴────────┴────────┘
Glimpse
函数 glimpse
是另一个显示 Dataframe 前几行值的函数,但其输出格式与 head
不同。在这里,输出的每一行对应一个单独的列,这使得检查更宽的 Dataframe 变得更容易。
print(df.glimpse(return_as_string=True))
Rows: 4
Columns: 4
$ name <str> 'Alice Archer', 'Ben Brown', 'Chloe Cooper', 'Daniel Donovan'
$ birthdate <date> 1997-01-10, 1985-02-15, 1983-03-22, 1981-04-30
$ weight <f64> 57.9, 72.5, 53.6, 83.1
$ height <f64> 1.56, 1.77, 1.65, 1.75
信息
glimpse
仅适用于 Python 用户。
Tail
函数 tail
显示 Dataframe 的最后几行。默认情况下,您会获得最后 5 行,但您也可以指定所需的行数,类似于 head
的工作方式。
shape: (3, 4)
┌────────────────┬────────────┬────────┬────────┐
│ name ┆ birthdate ┆ weight ┆ height │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ date ┆ f64 ┆ f64 │
╞════════════════╪════════════╪════════╪════════╡
│ Ben Brown ┆ 1985-02-15 ┆ 72.5 ┆ 1.77 │
│ Chloe Cooper ┆ 1983-03-22 ┆ 53.6 ┆ 1.65 │
│ Daniel Donovan ┆ 1981-04-30 ┆ 83.1 ┆ 1.75 │
└────────────────┴────────────┴────────┴────────┘
Sample
如果您认为 Dataframe 的前几行或最后几行不能代表您的数据,您可以使用 sample
从 Dataframe 中获取任意数量的随机选择行。请注意,这些行不一定按它们在 Dataframe 中出现的相同顺序返回。
shape: (2, 4)
┌────────────────┬────────────┬────────┬────────┐
│ name ┆ birthdate ┆ weight ┆ height │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ date ┆ f64 ┆ f64 │
╞════════════════╪════════════╪════════╪════════╡
│ Daniel Donovan ┆ 1981-04-30 ┆ 83.1 ┆ 1.75 │
│ Chloe Cooper ┆ 1983-03-22 ┆ 53.6 ┆ 1.65 │
└────────────────┴────────────┴────────┴────────┘
Describe
您还可以使用 describe
计算 Dataframe 中所有列的汇总统计信息。
print(df.describe())
describe
· 在 feature describe 上可用
// Not available in Rust
shape: (9, 5)
┌────────────┬────────────────┬─────────────────────┬───────────┬──────────┐
│ statistic ┆ name ┆ birthdate ┆ weight ┆ height │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ str ┆ str ┆ f64 ┆ f64 │
╞════════════╪════════════════╪═════════════════════╪═══════════╪══════════╡
│ count ┆ 4 ┆ 4 ┆ 4.0 ┆ 4.0 │
│ null_count ┆ 0 ┆ 0 ┆ 0.0 ┆ 0.0 │
│ mean ┆ null ┆ 1986-09-04 00:00:00 ┆ 66.775 ┆ 1.6825 │
│ std ┆ null ┆ null ┆ 13.560082 ┆ 0.097082 │
│ min ┆ Alice Archer ┆ 1981-04-30 ┆ 53.6 ┆ 1.56 │
│ 25% ┆ null ┆ 1983-03-22 ┆ 57.9 ┆ 1.65 │
│ 50% ┆ null ┆ 1985-02-15 ┆ 72.5 ┆ 1.75 │
│ 75% ┆ null ┆ 1985-02-15 ┆ 72.5 ┆ 1.75 │
│ max ┆ Daniel Donovan ┆ 1997-01-10 ┆ 83.1 ┆ 1.77 │
└────────────┴────────────────┴─────────────────────┴───────────┴──────────┘
Schema
在讨论数据(在 Dataframe 或其他地方)时,我们可以引用其 Schema。Schema 是列名或 Series 名称到这些列或 Series 数据类型的映射。
您可以使用 schema
检查 Dataframe 的 Schema。
print(df.schema)
println!("{:?}", df.schema());
Schema({'name': String, 'birthdate': Date, 'weight': Float64, 'height': Float64})
与 Series 类似,Polars 会在您创建 Dataframe 时推断其 Schema,但如果需要,您可以覆盖推断系统。
在 Python 中,您可以使用字典将列名映射到数据类型来指定显式 Schema。如果您不希望覆盖给定列的推断,则可以使用值 None
。
df = pl.DataFrame(
{
"name": ["Alice", "Ben", "Chloe", "Daniel"],
"age": [27, 39, 41, 43],
},
schema={"name": None, "age": pl.UInt8},
)
print(df)
shape: (4, 2)
┌────────┬─────┐
│ name ┆ age │
│ --- ┆ --- │
│ str ┆ u8 │
╞════════╪═════╡
│ Alice ┆ 27 │
│ Ben ┆ 39 │
│ Chloe ┆ 41 │
│ Daniel ┆ 43 │
└────────┴─────┘
如果您只需要覆盖某些列的推断,参数 schema_overrides
通常更方便,因为它允许您省略不想覆盖推断的列。
df = pl.DataFrame(
{
"name": ["Alice", "Ben", "Chloe", "Daniel"],
"age": [27, 39, 41, 43],
},
schema_overrides={"age": pl.UInt8},
)
print(df)
shape: (4, 2)
┌────────┬─────┐
│ name ┆ age │
│ --- ┆ --- │
│ str ┆ u8 │
╞════════╪═════╡
│ Alice ┆ 27 │
│ Ben ┆ 39 │
│ Chloe ┆ 41 │
│ Daniel ┆ 43 │
└────────┴─────┘
数据类型内部结构
Polars 利用 Arrow 列式格式进行数据定向。遵循此规范使得 Polars 能够以极小的开销与其他也使用 Arrow 规范的工具传输数据。
Polars 的大部分性能来自于其查询引擎、对查询计划执行的优化以及在运行表达式时采用的并行化。
浮点数
Polars 通常遵循 IEEE 754 浮点标准处理 Float32
和 Float64
,但有一些例外情况:
- 任何
NaN
都与任何其他NaN
比较相等,并且大于任何非NaN
值。 - 操作不保证零或
NaN
的符号,也不保证NaN
值的负载有任何特定行为。这不仅限于算术运算,例如,排序或分组操作可能会将所有零规范化为 +0,将所有NaN
规范化为无负载的正NaN
,以实现高效的相等性检查。
Polars 始终尝试为浮点计算提供合理准确的结果,但除非另有说明,否则不保证误差。一般来说,实现 100% 准确的结果在经济上是不可行的(需要比 64 位浮点数大得多的内部表示),因此总会存在一些误差。
附录:完整数据类型表
类型 | 详情 |
---|---|
布尔型 |
高效位打包的布尔类型。 |
Int8 , Int16 , Int32 , Int64 |
可变精度有符号整数类型。 |
UInt8 , UInt16 , UInt32 , UInt64 |
可变精度无符号整数类型。 |
Float32 , Float64 |
可变精度有符号浮点数。 |
Decimal |
具有可选精度和非负刻度的 Decimal 128 位类型。如果您需要对浮点数的精度及其操作进行精细控制,请使用此类型。有关十进制数据类型是什么的文档,请参阅 Python 的 decimal.Decimal 。 |
字符串 |
可变长度 UTF-8 编码的字符串数据,通常是人类可读的。 |
二进制 |
存储任意、可变长度的原始二进制数据。 |
日期 |
表示一个日历日期。 |
时间 |
表示一天中的时间。 |
日期时间 |
表示一个日历日期和一天中的时间。 |
持续时间 |
表示一个时间持续。 |
数组 |
每个 Series 具有已知固定形状的数组;类似于 NumPy 数组。了解更多关于数组和列表的区别以及如何使用它们。 |
列表 |
可变长度的同构一维容器。了解更多关于数组和列表的区别以及如何使用它们。 |
对象 |
封装任意 Python 对象。 |
分类 |
在运行时推断类别的字符串数据的有效编码。了解更多关于分类和枚举的区别以及如何使用它们。 |
枚举 |
预定字符串类别集的有效有序编码。了解更多关于分类和枚举的区别以及如何使用它们。 |
结构体 |
可以存储多个字段的复合产品类型。在其专门的文档部分了解更多关于数据类型 Struct 的信息。。 |
空值 |
表示空值。 |