Getting started
本章旨在帮助您开始使用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)和数据库(例如,postgres、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], # (公斤)
"height": [1.56, 1.77, 1.65, 1.75], # (米)
}
)
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], // (公斤)
"height" => [1.56, 1.77, 1.65, 1.75], // (米)
)
.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("无法创建文件");
CsvWriter::new(&mut file)
.include_header(true)
.with_separator(b',')
.finish(&mut df)?;
let df_csv = CsvReadOptions::default()
.with_infer_schema_length(None)
.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的上下文中,这个表达式才会具体化为一个包含结果的系列。
下面,我们将在不同的上下文中展示Polars表达式的示例:
selectwith_columnsfiltergroup_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)
.name()
.suffix("-5%"),
])
.collect()?;
println!("{}", result);
shape: (4, 3)
┌────────────────┬───────────┬───────────┐
│ name ┆ weight-5% ┆ height-5% │
│ --- ┆ --- ┆ --- │
│ str ┆ f64 ┆ f64 │
╞════════════════╪═══════════╪═══════════╡
│ Alice Archer ┆ 55.01 ┆ 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 上可用
// 如果你想要 Python 的 `maintain_order=True` 行为,请使用 `group_by_stable`。
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)
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)
.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], # (公斤)
"height": [1.76, 1.6, 1.66, 1.8], # (米)
}
)
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], // (公斤)
"height" => [1.76, 1.6, 1.66, 1.8], // (米)
)
.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 提供了垂直和水平连接,以及对角连接。您可以在用户指南的连接部分了解更多关于这些内容的信息。