入门
本章旨在帮助您快速开始使用 Polars。它涵盖了该库的所有基本特性和功能,使新用户可以轻松熟悉从初始安装和设置到核心功能的所有基础知识。如果您已经是高级用户或熟悉数据框,请随意跳到有关安装选项的下一章。
安装 Polars
pip install polars
cargo add polars -F lazy
# Or Cargo.toml
[dependencies]
polars = { version = "x", features = ["lazy", ...]}
读取与写入
Polars 支持常见文件格式(例如 CSV、JSON、Parquet)、云存储(S3、Azure Blob、BigQuery)和数据库(例如 PostgreSQL、MySQL)的读写。下面,我们将创建一个小型数据框,并展示如何将其写入磁盘并读回。
import polars as pl
import datetime as dt
df = pl.DataFrame(
{
"name": ["Alice Archer", "Ben Brown", "Chloe Cooper", "Daniel Donovan"],
"birthdate": [
dt.date(1997, 1, 10),
dt.date(1985, 2, 15),
dt.date(1983, 3, 22),
dt.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::*;
use polars::prelude::*;
let mut 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 │
└────────────────┴────────────┴────────┴────────┘
在下面的示例中,我们将数据框写入名为 output.csv
的 CSV 文件。之后,我们使用 read_csv
将其读回,然后打印结果进行检查。
df.write_csv("docs/assets/data/output.csv")
df_csv = pl.read_csv("docs/assets/data/output.csv", try_parse_dates=True)
print(df_csv)
CsvReader
· CsvWriter
· 在功能 csv 上可用
use std::fs::File;
let mut file = File::create("docs/assets/data/output.csv").expect("could not create file");
CsvWriter::new(&mut file)
.include_header(true)
.with_separator(b',')
.finish(&mut df)?;
let df_csv = CsvReadOptions::default()
.with_has_header(true)
.with_parse_options(CsvParseOptions::default().with_try_parse_dates(true))
.try_into_reader_with_file_path(Some("docs/assets/data/output.csv".into()))?
.finish()?;
println!("{df_csv}");
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 │
└────────────────┴────────────┴────────┴────────┘
有关 CSV 文件格式和其他数据格式的更多示例,请参阅用户指南的IO 部分。
表达式和上下文
表达式是 Polars 的主要优势之一,因为它们提供了一种模块化且灵活的数据转换方式。
以下是一个 Polars 表达式的示例
pl.col("weight") / (pl.col("height") ** 2)
正如您可能猜到的,此表达式将名为“weight”的列的值除以“height”列的值的平方,从而计算一个人的 BMI。请注意,上面的代码表达的是一个抽象计算:只有在 Polars 上下文中,该表达式才会具体化为包含结果的序列(series)。
下面,我们将展示在不同上下文中使用的 Polars 表达式的示例
select
with_columns
filter
group_by
select
上下文 select
允许您从数据框中选择和操作列。在最简单的情况下,您提供的每个表达式都将映射到结果数据框中的一列。
select
· alias
· dt namespace
result = df.select(
pl.col("name"),
pl.col("birthdate").dt.year().alias("birth_year"),
(pl.col("weight") / (pl.col("height") ** 2)).alias("bmi"),
)
print(result)
select
· alias
· dt namespace
· 在功能 temporal 上可用
let result = df
.clone()
.lazy()
.select([
col("name"),
col("birthdate").dt().year().alias("birth_year"),
(col("weight") / col("height").pow(2)).alias("bmi"),
])
.collect()?;
println!("{result}");
shape: (4, 3)
┌────────────────┬────────────┬───────────┐
│ name ┆ birth_year ┆ bmi │
│ --- ┆ --- ┆ --- │
│ str ┆ i32 ┆ f64 │
╞════════════════╪════════════╪═══════════╡
│ Alice Archer ┆ 1997 ┆ 23.791913 │
│ Ben Brown ┆ 1985 ┆ 23.141498 │
│ Chloe Cooper ┆ 1983 ┆ 19.687787 │
│ Daniel Donovan ┆ 1981 ┆ 27.134694 │
└────────────────┴────────────┴───────────┘
Polars 还支持一项名为“表达式扩展”的功能,其中一个表达式充当多个表达式的简写。在下面的示例中,我们使用表达式扩展来通过单个表达式操作“weight”和“height”列。使用表达式扩展时,您可以使用 .name.suffix
为原始列的名称添加后缀。
select
· alias
· name namespace
result = df.select(
pl.col("name"),
(pl.col("weight", "height") * 0.95).round(2).name.suffix("-5%"),
)
print(result)
select
· alias
· name namespace
· 在功能 lazy 上可用
let result = df
.clone()
.lazy()
.select([
col("name"),
(cols(["weight", "height"]) * lit(0.95))
.round(2, RoundMode::default())
.name()
.suffix("-5%"),
])
.collect()?;
println!("{result}");
shape: (4, 3)
┌────────────────┬───────────┬───────────┐
│ name ┆ weight-5% ┆ height-5% │
│ --- ┆ --- ┆ --- │
│ str ┆ f64 ┆ f64 │
╞════════════════╪═══════════╪═══════════╡
│ Alice Archer ┆ 55.0 ┆ 1.48 │
│ Ben Brown ┆ 68.88 ┆ 1.68 │
│ Chloe Cooper ┆ 50.92 ┆ 1.57 │
│ Daniel Donovan ┆ 78.94 ┆ 1.66 │
└────────────────┴───────────┴───────────┘
您可以查看用户指南的其他部分,以了解有关基本操作或表达式扩展中的列选择的更多信息。
with_columns
上下文 with_columns
与 select
上下文非常相似,但 with_columns
是向数据框添加列而不是选择列。请注意,结果数据框包含原始数据框的四列,以及 with_columns
内部表达式引入的两列新列。
result = df.with_columns(
birth_year=pl.col("birthdate").dt.year(),
bmi=pl.col("weight") / (pl.col("height") ** 2),
)
print(result)
let result = df
.clone()
.lazy()
.with_columns([
col("birthdate").dt().year().alias("birth_year"),
(col("weight") / col("height").pow(2)).alias("bmi"),
])
.collect()?;
println!("{result}");
shape: (4, 6)
┌────────────────┬────────────┬────────┬────────┬────────────┬───────────┐
│ name ┆ birthdate ┆ weight ┆ height ┆ birth_year ┆ bmi │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ date ┆ f64 ┆ f64 ┆ i32 ┆ f64 │
╞════════════════╪════════════╪════════╪════════╪════════════╪═══════════╡
│ Alice Archer ┆ 1997-01-10 ┆ 57.9 ┆ 1.56 ┆ 1997 ┆ 23.791913 │
│ Ben Brown ┆ 1985-02-15 ┆ 72.5 ┆ 1.77 ┆ 1985 ┆ 23.141498 │
│ Chloe Cooper ┆ 1983-03-22 ┆ 53.6 ┆ 1.65 ┆ 1983 ┆ 19.687787 │
│ Daniel Donovan ┆ 1981-04-30 ┆ 83.1 ┆ 1.75 ┆ 1981 ┆ 27.134694 │
└────────────────┴────────────┴────────┴────────┴────────────┴───────────┘
在上面的示例中,我们还决定使用命名表达式而不是 alias
方法来指定新列的名称。其他上下文,如 select
和 group_by
也接受命名表达式。
filter
上下文 filter
允许我们创建一个新的数据框,其中包含原始数据框行的子集。
result = df.filter(pl.col("birthdate").dt.year() < 1990)
print(result)
filter
· dt namespace
· 在功能 temporal 上可用
let result = df
.clone()
.lazy()
.filter(col("birthdate").dt().year().lt(lit(1990)))
.collect()?;
println!("{result}");
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 │
└────────────────┴────────────┴────────┴────────┘
您还可以将多个谓词表达式作为单独的参数提供,这比将它们与 &
组合在一起更方便。
result = df.filter(
pl.col("birthdate").is_between(dt.date(1982, 12, 31), dt.date(1996, 1, 1)),
pl.col("height") > 1.7,
)
print(result)
filter
· is_between
· 在功能 is_between 上可用
let result = df
.clone()
.lazy()
.filter(
col("birthdate")
.is_between(
lit(NaiveDate::from_ymd_opt(1982, 12, 31).unwrap()),
lit(NaiveDate::from_ymd_opt(1996, 1, 1).unwrap()),
ClosedInterval::Both,
)
.and(col("height").gt(lit(1.7))),
)
.collect()?;
println!("{result}");
shape: (1, 4)
┌───────────┬────────────┬────────┬────────┐
│ name ┆ birthdate ┆ weight ┆ height │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ date ┆ f64 ┆ f64 │
╞═══════════╪════════════╪════════╪════════╡
│ Ben Brown ┆ 1985-02-15 ┆ 72.5 ┆ 1.77 │
└───────────┴────────────┴────────┴────────┘
group_by
上下文 group_by
可用于将数据框中在一个或多个表达式上共享相同值的行进行分组。下面的示例计算了每个十年出生的人数。
group_by
· alias
· dt namespace
result = df.group_by(
(pl.col("birthdate").dt.year() // 10 * 10).alias("decade"),
maintain_order=True,
).len()
print(result)
group_by
· alias
· dt namespace
· 在功能 temporal 上可用
// Use `group_by_stable` if you want the Python behaviour of `maintain_order=True`.
let result = df
.clone()
.lazy()
.group_by([(col("birthdate").dt().year() / lit(10) * lit(10)).alias("decade")])
.agg([len()])
.collect()?;
println!("{result}");
shape: (2, 2)
┌────────┬─────┐
│ decade ┆ len │
│ --- ┆ --- │
│ i32 ┆ u32 │
╞════════╪═════╡
│ 1990 ┆ 1 │
│ 1980 ┆ 3 │
└────────┴─────┘
关键字参数 maintain_order
强制 Polars 以与原始数据框中相同的顺序呈现结果组。这会减慢分组操作,但在此处用于确保示例的可重现性。
在使用 group_by
上下文之后,我们可以使用 agg
对结果组进行聚合计算。
result = df.group_by(
(pl.col("birthdate").dt.year() // 10 * 10).alias("decade"),
maintain_order=True,
).agg(
pl.len().alias("sample_size"),
pl.col("weight").mean().round(2).alias("avg_weight"),
pl.col("height").max().alias("tallest"),
)
print(result)
let result = df
.clone()
.lazy()
.group_by([(col("birthdate").dt().year() / lit(10) * lit(10)).alias("decade")])
.agg([
len().alias("sample_size"),
col("weight")
.mean()
.round(2, RoundMode::default())
.alias("avg_weight"),
col("height").max().alias("tallest"),
])
.collect()?;
println!("{result}");
shape: (2, 4)
┌────────┬─────────────┬────────────┬─────────┐
│ decade ┆ sample_size ┆ avg_weight ┆ tallest │
│ --- ┆ --- ┆ --- ┆ --- │
│ i32 ┆ u32 ┆ f64 ┆ f64 │
╞════════╪═════════════╪════════════╪═════════╡
│ 1990 ┆ 1 ┆ 57.9 ┆ 1.56 │
│ 1980 ┆ 3 ┆ 69.73 ┆ 1.77 │
└────────┴─────────────┴────────────┴─────────┘
更复杂的查询
上下文及其内部的表达式可以串联起来,根据您的需要创建更复杂的查询。在下面的示例中,我们结合了到目前为止所见的一些上下文来创建一个更复杂的查询。
group_by
· agg
· select
· with_columns
· str namespace
· list namespace
result = (
df.with_columns(
(pl.col("birthdate").dt.year() // 10 * 10).alias("decade"),
pl.col("name").str.split(by=" ").list.first(),
)
.select(
pl.all().exclude("birthdate"),
)
.group_by(
pl.col("decade"),
maintain_order=True,
)
.agg(
pl.col("name"),
pl.col("weight", "height").mean().round(2).name.prefix("avg_"),
)
)
print(result)
group_by
· agg
· select
· with_columns
· str namespace
· list namespace
· 在功能 strings 上可用
let result = df
.clone()
.lazy()
.with_columns([
(col("birthdate").dt().year() / lit(10) * lit(10)).alias("decade"),
col("name").str().split(lit(" ")).list().first(),
])
.select([all().exclude(["birthdate"])])
.group_by([col("decade")])
.agg([
col("name"),
cols(["weight", "height"])
.mean()
.round(2, RoundMode::default())
.name()
.prefix("avg_"),
])
.collect()?;
println!("{result}");
shape: (2, 4)
┌────────┬────────────────────────────┬────────────┬────────────┐
│ decade ┆ name ┆ avg_weight ┆ avg_height │
│ --- ┆ --- ┆ --- ┆ --- │
│ i32 ┆ list[str] ┆ f64 ┆ f64 │
╞════════╪════════════════════════════╪════════════╪════════════╡
│ 1990 ┆ ["Alice"] ┆ 57.9 ┆ 1.56 │
│ 1980 ┆ ["Ben", "Chloe", "Daniel"] ┆ 69.73 ┆ 1.72 │
└────────┴────────────────────────────┴────────────┴────────────┘
合并数据框
Polars 提供了多种工具来组合两个数据框。在本节中,我们将展示连接和拼接的示例。
连接数据框
Polars 提供了多种不同的连接算法。下面的示例展示了当一列可用作唯一标识符以在数据框之间建立行对应关系时,如何使用左外连接来组合两个数据框。
df2 = pl.DataFrame(
{
"name": ["Ben Brown", "Daniel Donovan", "Alice Archer", "Chloe Cooper"],
"parent": [True, False, False, False],
"siblings": [1, 2, 3, 4],
}
)
print(df.join(df2, on="name", how="left"))
let df2: DataFrame = df!(
"name" => ["Ben Brown", "Daniel Donovan", "Alice Archer", "Chloe Cooper"],
"parent" => [true, false, false, false],
"siblings" => [1, 2, 3, 4],
)
.unwrap();
let result = df
.clone()
.lazy()
.join(
df2.clone().lazy(),
[col("name")],
[col("name")],
JoinArgs::new(JoinType::Left),
)
.collect()?;
println!("{result}");
shape: (4, 6)
┌────────────────┬────────────┬────────┬────────┬────────┬──────────┐
│ name ┆ birthdate ┆ weight ┆ height ┆ parent ┆ siblings │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ date ┆ f64 ┆ f64 ┆ bool ┆ i64 │
╞════════════════╪════════════╪════════╪════════╪════════╪══════════╡
│ Alice Archer ┆ 1997-01-10 ┆ 57.9 ┆ 1.56 ┆ false ┆ 3 │
│ Ben Brown ┆ 1985-02-15 ┆ 72.5 ┆ 1.77 ┆ true ┆ 1 │
│ Chloe Cooper ┆ 1983-03-22 ┆ 53.6 ┆ 1.65 ┆ false ┆ 4 │
│ Daniel Donovan ┆ 1981-04-30 ┆ 83.1 ┆ 1.75 ┆ false ┆ 2 │
└────────────────┴────────────┴────────┴────────┴────────┴──────────┘
Polars 提供了许多不同的连接算法,您可以在用户指南的连接部分了解它们。
拼接数据框
拼接数据框会创建一个更高或更宽的数据框,具体取决于所使用的方法。假设我们有第二个包含其他人数据的数据框,我们可以使用垂直拼接来创建一个更高的数据框。
df3 = pl.DataFrame(
{
"name": ["Ethan Edwards", "Fiona Foster", "Grace Gibson", "Henry Harris"],
"birthdate": [
dt.date(1977, 5, 10),
dt.date(1975, 6, 23),
dt.date(1973, 7, 22),
dt.date(1971, 8, 3),
],
"weight": [67.9, 72.5, 57.6, 93.1], # (kg)
"height": [1.76, 1.6, 1.66, 1.8], # (m)
}
)
print(pl.concat([df, df3], how="vertical"))
let df3: DataFrame = df!(
"name" => ["Ethan Edwards", "Fiona Foster", "Grace Gibson", "Henry Harris"],
"birthdate" => [
NaiveDate::from_ymd_opt(1977, 5, 10).unwrap(),
NaiveDate::from_ymd_opt(1975, 6, 23).unwrap(),
NaiveDate::from_ymd_opt(1973, 7, 22).unwrap(),
NaiveDate::from_ymd_opt(1971, 8, 3).unwrap(),
],
"weight" => [67.9, 72.5, 57.6, 93.1], // (kg)
"height" => [1.76, 1.6, 1.66, 1.8], // (m)
)
.unwrap();
let result = concat(
[df.clone().lazy(), df3.clone().lazy()],
UnionArgs::default(),
)?
.collect()?;
println!("{result}");
shape: (8, 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 │
│ Ethan Edwards ┆ 1977-05-10 ┆ 67.9 ┆ 1.76 │
│ Fiona Foster ┆ 1975-06-23 ┆ 72.5 ┆ 1.6 │
│ Grace Gibson ┆ 1973-07-22 ┆ 57.6 ┆ 1.66 │
│ Henry Harris ┆ 1971-08-03 ┆ 93.1 ┆ 1.8 │
└────────────────┴────────────┴────────┴────────┘
Polars 提供垂直拼接、水平拼接以及对角拼接。您可以在用户指南的拼接部分了解更多信息。