最強(qiáng) Pandas 平替 -- Polars

“
閱讀本文大概需要 9 分鐘。
Polars 是一個(gè)用于操作結(jié)構(gòu)化數(shù)據(jù)的高性能 DataFrame 庫(kù),可以說是平替 pandas 最有潛質(zhì)的包。Polars 其核心部分是用 Rust 編寫的,但該庫(kù)也提供了 Python 接口。它的主要特點(diǎn)包括:
-
快速: Polars 是從零開始編寫的,緊密與機(jī)器結(jié)合,沒有外部依賴。
-
I/O: 對(duì)所有常見數(shù)據(jù)存儲(chǔ)層提供一流支持:本地、云存儲(chǔ)和數(shù)據(jù)庫(kù)。
-
易于使用: 以原始意圖編寫查詢。Polars 在內(nèi)部會(huì)使用其查詢優(yōu)化器確定執(zhí)行最有效的方式。
-
離線處理: Polars 支持通過其流式 API 進(jìn)行離線數(shù)據(jù)轉(zhuǎn)換。這使您能夠處理結(jié)果,而無需同時(shí)將所有數(shù)據(jù)存儲(chǔ)在內(nèi)存中。
-
并行處理: Polars 通過在可用的 CPU 核心之間分配工作負(fù)載,充分利用計(jì)算機(jī)性能,而無需額外配置。
-
矢量化查詢引擎: Polars 使用 Apache Arrow,一種列式數(shù)據(jù)格式,以矢量化方式處理查詢。它使用 SIMD 來優(yōu)化CPU使用。
User guide: https://pola-rs.github.io/polars/user-guide/
API reference: https://pola-rs.github.io/polars/py-polars/html/reference/io.html
介紹
Polars 的目標(biāo)是提供一個(gè)閃電般快速的 DataFrame 庫(kù),具有以下特點(diǎn):
-
利用計(jì)算機(jī)上所有可用的核心。
-
通過優(yōu)化查詢來減少不必要的工作/內(nèi)存分配。
-
處理比可用 RAM 更大得多的數(shù)據(jù)集。
-
具有一致且可預(yù)測(cè)的 API。
-
具有嚴(yán)格的模式(在運(yùn)行查詢之前應(yīng)該知道數(shù)據(jù)類型)。
Polars 是用 Rust 編寫的,這使得它具有 C/C++ 性能,并允許它完全控制查詢引擎中的性能關(guān)鍵部分。因此,Polars 為此付出了很大的努力:
-
減少冗余的復(fù)制。
-
高效地遍歷內(nèi)存緩存。
-
在并行性中最小化爭(zhēng)用。
-
以塊處理數(shù)據(jù)。
-
重用內(nèi)存分配。
# 1. 基礎(chǔ)
Series & DataFrames
Series 是一個(gè)一維數(shù)據(jù)結(jié)構(gòu)。在一個(gè) Series 中,所有元素都具有相同的數(shù)據(jù)類型(例如,整數(shù)、字符串)。下面的片段展示了如何創(chuàng)建一個(gè)簡(jiǎn)單的帶有名稱的 Series 對(duì)象。
import polars as pl
import numpy as np
s = pl.Series("a", [1, 2, 3, 4, 5])
print(s)
s = pl.Series("a", [1, 2, 3, 4, 5])
print(s.min())
print(s.max())
s = pl.Series("a", ["polar", "bear", "arctic", "polar fox", "polar bear"])
s2 = s.str.replace("polar", "pola")
print(s2)
from datetime import date
start = date(2001, 1, 1)
stop = date(2001, 1, 9)
s = pl.date_range(start, stop, interval="2d", eager=True)
print(s.dt.day())
DataFrame 是一個(gè)二維數(shù)據(jù)結(jié)構(gòu),由一個(gè)或多個(gè) Series 支持,可以看作是對(duì)一系列(例如列表)Series的抽象。在 DataFrame 上可以執(zhí)行的操作與在 SQL 查詢中執(zhí)行的操作非常相似。您可以進(jìn)行 GROUP BY、JOIN、PIVOT,還可以定義自定義函數(shù)。
from datetime import datetime
df = pl.DataFrame(
{
"integer": [1, 2, 3, 4, 5],
"date": [
datetime(2022, 1, 1),
datetime(2022, 1, 2),
datetime(2022, 1, 3),
datetime(2022, 1, 4),
datetime(2022, 1, 5),
],
"float": [4.0, 5.0, 6.0, 7.0, 8.0],
}
)
print(df)
print(df.head(3))
print(df.describe())
Reading & writing
import polars as pl
from datetime import datetime
df = pl.DataFrame(
{
"integer": [1, 2, 3],
"date": [
datetime(2022, 1, 1),
datetime(2022, 1, 2),
datetime(2022, 1, 3),
],
"float": [4.0, 5.0, 6.0],
}
)
print(df)
df.write_csv("output.csv")
df_csv = pl.read_csv("output.csv")
print(df_csv)
df_csv = pl.read_csv("output.csv", try_parse_dates=True)
print(df_csv)
Expressions
import polars as pl
# 創(chuàng)建一個(gè)簡(jiǎn)單的 DataFrame
data = {'column1': [1, 2, 3],
'column2': ['a', 'b', 'c']}
df = pl.DataFrame(data)
# 使用表達(dá)式進(jìn)行選擇
selected_df = df.select(['column1'])
# 使用表達(dá)式進(jìn)行過濾
filtered_df = df.filter(df['column1'] > 1)
selected_df
filtered_df
# 2. 拼接
df = pl.DataFrame(
{
"a": np.arange(0, 8),
"b": np.random.rand(8),
"d": [1, 2.0, np.NaN, np.NaN, 0, -5, -42, None],
}
)
df2 = pl.DataFrame(
{
"x": np.arange(0, 8),
"y": ["A", "A", "A", "B", "B", "C", "X", "X"],
}
)
joined = df.join(df2, left_on="a", right_on="x")
print(joined)
stacked = df.hstack(df2)
print(stacked)
# 3. 概念
Data types
Polars 完全基于 Arrow 數(shù)據(jù)類型,并由 Arrow 內(nèi)存數(shù)組支持。這使得數(shù)據(jù)處理在緩存效率和跨進(jìn)程通信方面得到良好支持。大多數(shù)數(shù)據(jù)類型都與 Arrow 的實(shí)現(xiàn)完全一致,但有一些例外,如 Utf8(實(shí)際上是 LargeUtf8)、Categorical 和 Object(支持有限)等。以下是一些數(shù)據(jù)類型:
| 分組 | 類型 | 詳細(xì)信息 |
|---|---|---|
| 數(shù)值 | Int8 |
8 位有符號(hào)整數(shù)。 |
| |
Int16 |
16 位有符號(hào)整數(shù)。 |
| |
Int32 |
32 位有符號(hào)整數(shù)。 |
| |
Int64 |
64 位有符號(hào)整數(shù)。 |
| |
UInt8 |
8 位無符號(hào)整數(shù)。 |
| |
UInt16 |
16 位無符號(hào)整數(shù)。 |
| |
UInt32 |
32 位無符號(hào)整數(shù)。 |
| |
UInt64 |
64 位無符號(hào)整數(shù)。 |
| |
Float32 |
32 位浮點(diǎn)數(shù)。 |
| |
Float64 |
64 位浮點(diǎn)數(shù)。 |
| 嵌套 | Struct |
結(jié)構(gòu)數(shù)組表示為 Vec<Series>,用于在單個(gè)列中打包多個(gè)/異構(gòu)值。 |
| |
List |
列表數(shù)組包含包含列表值的子數(shù)組和一個(gè)偏移數(shù)組(在內(nèi)部實(shí)際上是 Arrow 的 LargeList)。 |
| 時(shí)間 | Date |
日期表示,內(nèi)部表示為距離 UNIX 紀(jì)元的天數(shù),由 32 位有符號(hào)整數(shù)編碼。 |
| |
Datetime |
日期時(shí)間表示,內(nèi)部表示為距離 UNIX 紀(jì)元的微秒數(shù),由 64 位有符號(hào)整數(shù)編碼。 |
| |
Duration |
表示時(shí)間差的類型,內(nèi)部表示為微秒。通過減去 Date/Datetime 創(chuàng)建。 |
| |
Time |
時(shí)間表示,內(nèi)部表示為距午夜的納秒數(shù)。 |
| 其他 | Boolean |
布爾類型,實(shí)際上是按位打包的。 |
| |
Utf8 |
字符串?dāng)?shù)據(jù)(實(shí)際上在內(nèi)部是 Arrow 的 LargeUtf8)。 |
| |
Binary |
以字節(jié)形式存儲(chǔ)數(shù)據(jù)。 |
| |
Object |
有限支持的數(shù)據(jù)類型,可以是任何值。 |
| |
Categorical |
一組字符串的分類編碼。 |
Contexts
Polars 已經(jīng)開發(fā)了自己的領(lǐng)域特定語言(DSL)用于數(shù)據(jù)轉(zhuǎn)換。該語言非常易于使用,允許進(jìn)行復(fù)雜的查詢,同時(shí)保持人類可讀性。該語言的兩個(gè)核心組件是上下文(Contexts)和表達(dá)式(Expressions),我們將在下一部分介紹表達(dá)式。
正如名稱所示,上下文指的是需要評(píng)估表達(dá)式的上下文。有三個(gè)主要的上下文 [^1^]:
-
選擇(Selection):
df.select([..]),df.with_columns([..]) -
過濾(Filtering):
df.filter() -
分組/聚合(Group by / Aggregation):
df.group_by(..).agg([..])
df = pl.DataFrame(
{
"nrs": [1, 2, 3, None, 5],
"names": ["foo", "ham", "spam", "egg", None],
"random": np.random.rand(5),
"groups": ["A", "A", "B", "C", "B"],
}
)
print(df)
# 基于df 進(jìn)行計(jì)算,得到新的表格
out = df.select(
pl.sum("nrs"), # nrs的和
pl.col("names").sort(), # names排序后的結(jié)果
pl.col("names").first().alias("first name"), # names第一個(gè)值
(pl.mean("nrs") * 10).alias("10xnrs"), # nrs的均值 * 10
)
print(out)
# 原始df 新增列
df = df.with_columns(
pl.sum("nrs").alias("nrs_sum"),
pl.col("random").count().alias("count"),
)
print(df)
out = df.filter(pl.col("nrs") > 2)
print(out)
out = df.group_by("groups").agg(
pl.sum("nrs"), # sum nrs by groups
pl.col("random").count().alias("count"), # count group members
# sum random where name != null
pl.col("random").filter(pl.col("names").is_not_null()).sum().name.suffix("_sum"),
pl.col("names").reverse().alias("reversed names"),
)
print(out)
Lazy / eager API
Polars支持兩種操作模式:lazy(延遲)和eager(即時(shí))。在eager API中,查詢會(huì)立即執(zhí)行,而在lazy API中,查詢只有在“需要”時(shí)才會(huì)被評(píng)估。
!wget https://mirror.coggle.club/dataset/heart.csv
!head heart.csv
df = pl.read_csv("heart.csv")
df_small = df.filter(pl.col("age") > 5)
df_agg = df_small.group_by("sex").agg(pl.col("chol").mean())
print(df_agg)
q = (
pl.scan_csv("heart.csv")
.filter(pl.col("age") > 5)
.group_by("sex")
.agg(pl.col("chol").mean())
)
# 生成了計(jì)算邏輯
df = q.collect() # 真正計(jì)算
print(df)
Streaming API
https://pola-rs.github.io/polars/user-guide/concepts/streaming/
Lazy API的另一個(gè)額外好處是它允許以流式方式執(zhí)行查詢。與一次性處理所有數(shù)據(jù)不同,Polars可以按批執(zhí)行查詢,使您能夠處理大于內(nèi)存的數(shù)據(jù)集。
q = (
pl.scan_csv("heart.csv")
.filter(pl.col("age") > 5)
.group_by("sex")
.agg(pl.col("chol").mean())
)
df = q.collect(streaming=True)
print(df)
# 4. 表達(dá)式
Basic operators
df = pl.DataFrame(
{
"nrs": [1, 2, 3, None, 5],
"names": ["foo", "ham", "spam", "egg", None],
"random": np.random.rand(5),
"groups": ["A", "A", "B", "C", "B"],
}
)
print(df)
df_numerical = df.select(
(pl.col("nrs") + 5).alias("nrs + 5"),
(pl.col("nrs") - 5).alias("nrs - 5"),
(pl.col("nrs") * pl.col("random")).alias("nrs * random"),
(pl.col("nrs") / pl.col("random")).alias("nrs / random"),
)
print(df_numerical)
df_logical = df.select(
(pl.col("nrs") > 1).alias("nrs > 1"),
(pl.col("random") <= 0.5).alias("random <= .5"),
(pl.col("nrs") != 1).alias("nrs != 1"),
(pl.col("nrs") == 1).alias("nrs == 1"),
((pl.col("random") <= 0.5) & (pl.col("nrs") > 1)).alias("and_expr"), # and
((pl.col("random") <= 0.5) | (pl.col("nrs") > 1)).alias("or_expr"), # or
)
print(df_logical)
Column selections
from datetime import date, datetime
df = pl.DataFrame(
{
"id": [9, 4, 2],
"place": ["Mars", "Earth", "Saturn"],
"date": pl.date_range(date(2022, 1, 1), date(2022, 1, 3), "1d", eager=True),
"sales": [33.4, 2142134.1, 44.7],
"has_people": [False, True, False],
"logged_at": pl.datetime_range(
datetime(2022, 12, 1), datetime(2022, 12, 1, 0, 0, 2), "1s", eager=True
),
}
).with_row_count("rn")
print(df)
out = df.select(pl.col("*"))
# Is equivalent to
out = df.select(pl.all())
print(out)
out = df.select(pl.col("*").exclude("logged_at", "rn"))
print(out)
out = df.select(pl.col("date", "logged_at").dt.to_string("%Y-%h-%d"))
print(out)
out = df.select(pl.col("^.*(as|sa).*$"))
print(out)
out = df.select(pl.col(pl.Int64, pl.UInt32, pl.Boolean).n_unique())
print(out)
import polars.selectors as cs
out = df.select(cs.numeric() - cs.first())
print(out)
out = df.select(cs.contains("rn"), cs.matches(".*_.*"))
print(out)
Functions
df = pl.DataFrame(
{
"nrs": [1, 2, 3, None, 5],
"names": ["foo", "ham", "spam", "egg", "spam"],
"random": np.random.rand(5),
"groups": ["A", "A", "B", "C", "B"],
}
)
print(df)
df_samename = df.select(pl.col("nrs") + 5)
print(df_samename)
df_alias = df.select(
(pl.col("nrs") + 5).alias("nrs + 5"),
(pl.col("nrs") - 5).alias("nrs - 5"),
)
print(df_alias)
df_alias = df.select(
pl.col("names").n_unique().alias("unique"),
pl.approx_n_unique("names").alias("unique_approx"),
)
print(df_alias)
df_conditional = df.select(
pl.col("nrs"),
pl.when(pl.col("nrs") > 2)
.then(pl.lit(True))
.otherwise(pl.lit(False))
.alias("conditional"),
)
print(df_conditional)
# 5. 轉(zhuǎn)換
類型轉(zhuǎn)換(Casting)將列的底層 DataType 轉(zhuǎn)換為新的數(shù)據(jù)類型。Polars 使用 Arrow 在內(nèi)存中管理數(shù)據(jù),并依賴于 Rust 實(shí)現(xiàn)中的計(jì)算核心 來執(zhí)行轉(zhuǎn)換。類型轉(zhuǎn)換通過 cast() 方法實(shí)現(xiàn)。
cast 方法包括一個(gè) strict 參數(shù),該參數(shù)確定當(dāng) Polars 遇到無法從源 DataType 轉(zhuǎn)換為目標(biāo) DataType 的值時(shí)的行為。默認(rèn)情況下,strict=True,這意味著 Polars 將引發(fā)錯(cuò)誤,通知用戶轉(zhuǎn)換失敗,并提供無法轉(zhuǎn)換的值的詳細(xì)信息。另一方面,如果 strict=False,無法轉(zhuǎn)換為目標(biāo) DataType 的任何值都將悄悄地轉(zhuǎn)換為 null。
df = pl.DataFrame(
{
"integers": [1, 2, 3, 4, 5],
"big_integers": [1, 10000002, 3, 10000004, 10000005],
"floats": [4.0, 5.0, 6.0, 7.0, 8.0],
"floats_with_decimal": [4.532, 5.5, 6.5, 7.5, 8.5],
}
)
print(df)
out = df.select(
pl.col("integers").cast(pl.Float32).alias("integers_as_floats"),
pl.col("floats").cast(pl.Int32).alias("floats_as_integers"),
pl.col("floats_with_decimal")
.cast(pl.Int32)
.alias("floats_with_decimal_as_integers"),
)
print(out)
out = df.select(
pl.col("integers").cast(pl.Int16).alias("integers_smallfootprint"),
pl.col("floats").cast(pl.Float32).alias("floats_smallfootprint"),
)
print(out)
df = pl.DataFrame(
{
"integers": [1, 2, 3, 4, 5],
"float": [4.0, 5.03, 6.0, 7.0, 8.0],
"floats_as_string": ["4.0", "5.0", "6.0", "7.0", "8.0"],
}
)
out = df.select(
pl.col("integers").cast(pl.Utf8),
pl.col("float").cast(pl.Utf8),
pl.col("floats_as_string").cast(pl.Float64),
)
print(out)
df = pl.DataFrame(
{
"integers": [-1, 0, 2, 3, 4],
"floats": [0.0, 1.0, 2.0, 3.0, 4.0],
"bools": [True, False, True, False, True],
}
)
out = df.select(pl.col("integers").cast(pl.Boolean), pl.col("floats").cast(pl.Boolean))
print(out)
from datetime import date, datetime
df = pl.DataFrame(
{
"date": pl.date_range(date(2022, 1, 1), date(2022, 1, 5), eager=True),
"datetime": pl.datetime_range(
datetime(2022, 1, 1), datetime(2022, 1, 5), eager=True
),
}
)
out = df.select(pl.col("date").cast(pl.Int64), pl.col("datetime").cast(pl.Int64))
print(out)
Strings
df = pl.DataFrame({"animal": ["Crab", "cat and dog", "rab$bit", None]})
out = df.select(
pl.col("animal").str.len_bytes().alias("byte_count"),
pl.col("animal").str.len_chars().alias("letter_count"),
)
print(out)
out = df.select(
pl.col("animal"),
pl.col("animal").str.contains("cat|bit").alias("regex"),
pl.col("animal").str.contains("rab$", literal=True).alias("literal"),
pl.col("animal").str.starts_with("rab").alias("starts_with"),
pl.col("animal").str.ends_with("dog").alias("ends_with"),
)
print(out)
Aggregation
https://pola-rs.github.io/polars/user-guide/expressions/aggregation/
df = pl.read_csv("heart.csv")
print(df)
q = (
df.lazy()
.group_by("sex")
.agg(
pl.count(),
pl.col("cp"),
pl.first("caa"),
)
.sort("count", descending=True)
.limit(5)
)
df = q.collect()
print(df)
q = (
df.lazy()
.group_by("sex")
.agg(
(pl.col("cp") == 1).sum().alias("anti"),
(pl.col("cp") == 2).sum().alias("pro"),
)
.sort("pro", descending=True)
.limit(5)
)
df = q.collect()
print(df)
# 6. 缺失值
df = pl.DataFrame(
{
"value": [1, None],
},
)
print(df)
null_count_df = df.null_count()
print(null_count_df)
df = pl.DataFrame(
{
"col1": [1, 2, 3],
"col2": [1, None, 3],
},
)
print(df)
fill_literal_df = df.with_columns(
pl.col("col2").fill_null(pl.lit(2)),
)
print(fill_literal_df)
fill_forward_df = df.with_columns(
pl.col("col2").fill_null(strategy="forward"),
)
print(fill_forward_df)
fill_median_df = df.with_columns(
pl.col("col2").fill_null(pl.median("col2")),
)
print(fill_median_df)
fill_interpolation_df = df.with_columns(
pl.col("col2").interpolate(),
)
print(fill_interpolation_df)
Window functions
https://pola-rs.github.io/polars/user-guide/expressions/window/
!wget https://cdn.coggle.club/Pokemon.csv
!head Pokemon.csv
# then let's load some csv data with information about pokemon
df = pl.read_csv("Pokemon.csv")
print(df.head())
out = df.select(
"Type 1",
"Type 2",
pl.col("Attack").mean().over("Type 1").alias("avg_attack_by_type"),
pl.col("Defense")
.mean()
.over(["Type 1", "Type 2"])
.alias("avg_defense_by_type_combination"),
pl.col("Attack").mean().alias("avg_attack"),
)
print(out)
filtered = df.filter(pl.col("Type 2") == "Psychic").select(
"Name",
"Type 1",
"Speed",
)
print(filtered)
out = filtered.with_columns(
pl.col(["Name", "Speed"]).sort_by("Speed", descending=True).over("Type 1"),
)
print(out)
Lists and Arrays
weather = pl.DataFrame(
{
"station": ["Station " + str(x) for x in range(1, 6)],
"temperatures": [
"20 5 5 E1 7 13 19 9 6 20",
"18 8 16 11 23 E2 8 E2 E2 E2 90 70 40",
"19 24 E9 16 6 12 10 22",
"E2 E0 15 7 8 10 E1 24 17 13 6",
"14 8 E0 16 22 24 E1",
],
}
)
print(weather)
out = weather.with_columns(pl.col("temperatures").str.split(" "))
print(out)
out = weather.with_columns(pl.col("temperatures").str.split(" ")).explode(
"temperatures"
)
print(out)
out = weather.with_columns(pl.col("temperatures").str.split(" ")).with_columns(
pl.col("temperatures").list.head(3).alias("top3"),
pl.col("temperatures").list.slice(-3, 3).alias("bottom_3"),
pl.col("temperatures").list.len().alias("obs"),
)
print(out)
# 7. 變形
Joins
| 策略 | 描述 |
|---|---|
inner |
返回兩個(gè)數(shù)據(jù)框中具有匹配鍵的行。左框或右框中的非匹配行將被丟棄。 |
left |
返回左數(shù)據(jù)框中的所有行,無論是否在右數(shù)據(jù)框中找到匹配項(xiàng)。非匹配行的右列將被填充為null。 |
outer |
返回左右兩個(gè)數(shù)據(jù)框中的所有行。如果在一個(gè)框中找不到匹配項(xiàng),則從另一個(gè)框中的列將被填充為null。 |
cross |
返回左框中的所有行與右框中的所有行的笛卡爾積。重復(fù)的行將被保留;左框與右框的交叉連接的表長(zhǎng)度始終為len(A) × len(B)。 |
asof |
在此連接中,匹配是根據(jù)最近的鍵而不是相等的鍵執(zhí)行的左連接。 |
semi |
返回左框中具有與右框中相同的連接鍵的所有行。 |
anti |
返回左框中連接鍵不在右框中出現(xiàn)的所有行。 |
df_customers = pl.DataFrame(
{
"customer_id": [1, 2, 3],
"name": ["Alice", "Bob", "Charlie"],
}
)
print(df_customers)
df_orders = pl.DataFrame(
{
"order_id": ["a", "b", "c"],
"customer_id": [1, 2, 2],
"amount": [100, 200, 300],
}
)
print(df_orders)
df = df_customers.join(df_orders, on="customer_id", how="inner")
print(df)
df = df_customers.join(df_orders, on="customer_id", how="left")
print(df)
df = df_customers.join(df_orders, on="customer_id", how="outer")
print(df)
df = df_customers.join(df_orders, on="customer_id", how="cross")
print(df)
df_cars = pl.DataFrame(
{
"id": ["a", "b", "c"],
"make": ["ford", "toyota", "bmw"],
}
)
print(df_cars)
df_repairs = pl.DataFrame(
{
"id": ["c", "c"],
"cost": [100, 200],
}
)
print(df_repairs)
df = df_cars.join(df_repairs, on="id", how="inner")
print(df)
df = df_cars.join(df_repairs, on="id", how="semi")
print(df)
df = df_cars.join(df_repairs, on="id", how="anti")
print(df)
df_trades = pl.DataFrame(
{
"time": [
datetime(2020, 1, 1, 9, 1, 0),
datetime(2020, 1, 1, 9, 1, 0),
datetime(2020, 1, 1, 9, 3, 0),
datetime(2020, 1, 1, 9, 6, 0),
],
"stock": ["A", "B", "B", "C"],
"trade": [101, 299, 301, 500],
}
)
print(df_trades)
df_quotes = pl.DataFrame(
{
"time": [
datetime(2020, 1, 1, 9, 0, 0),
datetime(2020, 1, 1, 9, 2, 0),
datetime(2020, 1, 1, 9, 4, 0),
datetime(2020, 1, 1, 9, 6, 0),
],
"stock": ["A", "B", "C", "A"],
"quote": [100, 300, 501, 102],
}
)
print(df_quotes)
df_asof_join = df_trades.join_asof(df_quotes, on="time", by="stock")
print(df_asof_join)
df_asof_tolerance_join = df_trades.join_asof(
df_quotes, on="time", by="stock", tolerance="1m"
)
print(df_asof_tolerance_join)
Concatenation
df_v1 = pl.DataFrame(
{
"a": [1],
"b": [3],
}
)
df_v2 = pl.DataFrame(
{
"a": [2],
"b": [4],
}
)
df_vertical_concat = pl.concat(
[
df_v1,
df_v2,
],
how="vertical",
)
print(df_vertical_concat)
df_h1 = pl.DataFrame(
{
"l1": [1, 2],
"l2": [3, 4],
}
)
df_h2 = pl.DataFrame(
{
"r1": [5, 6],
"r2": [7, 8],
"r3": [9, 10],
}
)
df_horizontal_concat = pl.concat(
[
df_h1,
df_h2,
],
how="horizontal",
)
print(df_horizontal_concat)
Pivots
df = pl.DataFrame(
{
"foo": ["A", "A", "B", "B", "C"],
"N": [1, 2, 2, 4, 2],
"bar": ["k", "l", "m", "n", "o"],
}
)
print(df)
out = df.pivot(index="foo", columns="bar", values="N", aggregate_function="first")
print(out)
q = (
df.lazy()
.collect()
.pivot(index="foo", columns="bar", values="N", aggregate_function="first")
.lazy()
)
out = q.collect()
print(out)
Melts
import polars as pl
df = pl.DataFrame(
{
"A": ["a", "b", "a"],
"B": [1, 3, 5],
"C": [10, 11, 12],
"D": [2, 4, 6],
}
)
print(df)
out = df.melt(id_vars=["A", "B"], value_vars=["C", "D"])
print(out)
本文來源: 網(wǎng)絡(luò)。 僅用于傳遞和分享 更多信息,并不代表本平臺(tái)贊同其觀點(diǎn)和對(duì)其真實(shí)性負(fù)責(zé),版權(quán)歸原作者所有,如有侵權(quán)請(qǐng)聯(lián)系我們刪除。
