拼接DataFrame
- 具有相同列的两个DataFrame可以垂直连接, 拼接成更长的DataFrame.
- 两个具有不重叠列的DataFrame可以水平连接, 拼接成更宽的DataFrame.
- 两个行数和列数不同的的DataFrame可以对联拼接, 从而形成一个可能更长或更宽的DataFrame. 列名重叠的部分将垂直连接,
列名不重叠的部分将添加新的行和列. 缺失值设置为
null
垂直拼接
使用concat
函数和how="vertical"
参数即可, 但是要注意, 如果没有相同的列名, 则会报错(比如一个3列一个2列, 或者都是2列但是列名不完全相同).
垂直拼接不要求两个DataFrame的行数相同, 因为我们的目的是垂直拼接, 只要一样宽即可.
1import polars as pl
2
3df1 = pl.DataFrame({
4 "a":[1,2],
5 "b":[11,22]
6})
7df2 = pl.DataFrame({
8 "a":[3,4,5],
9 "b":[33,44,55]
10})
11res = pl.concat(
12 [df1,df2],
13 how="vertical"
14)
15print(res)
1shape: (5, 2)
2┌─────┬─────┐
3│ a ┆ b │
4│ --- ┆ --- │
5│ i64 ┆ i64 │
6╞═════╪═════╡
7│ 1 ┆ 11 │
8│ 2 ┆ 22 │
9│ 3 ┆ 33 │
10│ 4 ┆ 44 │
11│ 5 ┆ 55 │
12└─────┴─────┘
如果我们只想拼接两个DataFrame中的"a"
列也是可以的, 来看代码
1import polars as pl
2
3df1 = pl.DataFrame({
4 "a":[1,2],
5 "b":[11,22]
6})
7df2 = pl.DataFrame({
8 "a":[3,4,5],
9 "b":[33,44,55]
10})
11res = pl.concat(
12 [
13 df1.select(pl.col("a")),
14 df2.select(pl.col("a"))
15 ],
16 how="vertical"
17)
18print(res)
水平拼接
指定参数how="horizontal"
1df_h1 = pl.DataFrame(
2 {
3 "l1": [1, 2],
4 "l2": [3, 4],
5 }
6)
7df_h2 = pl.DataFrame(
8 {
9 "r1": [5, 6],
10 "r2": [7, 8],
11 "r3": [9, 10],
12 }
13)
14res = pl.concat(
15 [
16 df_h1,
17 df_h2,
18 ],
19 how="horizontal",
20)
21print(res)
1shape: (2, 5)
2┌─────┬─────┬─────┬─────┬─────┐
3│ l1 ┆ l2 ┆ r1 ┆ r2 ┆ r3 │
4│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
5│ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 │
6╞═════╪═════╪═════╪═════╪═════╡
7│ 1 ┆ 3 ┆ 5 ┆ 7 ┆ 9 │
8│ 2 ┆ 4 ┆ 6 ┆ 8 ┆ 10 │
9└─────┴─────┴─────┴─────┴─────┘
行数不同, 用null
填充
如果两个DataFrame的高度不相同, 则会使用null
来填充, 我们看下面的代码, 注意高亮的部分
1df_h1 = pl.DataFrame(
2 {
3 "l1": [1, 2, 3],
4 "l2": [3, 4, 5],
5 }
6)
7df_h2 = pl.DataFrame(
8 {
9 "r1": [5, 6],
10 "r2": [7, 8],
11 "r3": [9, 10],
12 }
13)
14res = pl.concat(
15 [
16 df_h1,
17 df_h2,
18 ],
19 how="horizontal",
20)
21print(res)
1shape: (3, 5)
2┌─────┬─────┬──────┬──────┬──────┐
3│ l1 ┆ l2 ┆ r1 ┆ r2 ┆ r3 │
4│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
5│ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 │
6╞═════╪═════╪══════╪══════╪══════╡
7│ 1 ┆ 3 ┆ 5 ┆ 7 ┆ 9 │
8│ 2 ┆ 4 ┆ 6 ┆ 8 ┆ 10 │
9│ 3 ┆ 5 ┆ null ┆ null ┆ null │
10└─────┴─────┴──────┴──────┴──────┘
包含相同列名会报错
1df_h1 = pl.DataFrame(
2 {
3 "l1": [1, 2],
4 "l2": [3, 4],
5 }
6)
7df_h2 = pl.DataFrame(
8 {
9 "l1": [5, 6],
10 "r2": [7, 8],
11 "r3": [9, 10],
12 }
13)
14res = pl.concat(
15 [
16 df_h1,
17 df_h2,
18 ],
19 how="horizontal",
20)
21print(res)
1省略
2DuplicateError: column with name 'l1' has more than one occurrence
对角拼接
指定参数how="diagonal"
, 使新的DataFrame更长/更宽
1df_d1 = pl.DataFrame(
2 {
3 "a": [1],
4 "b": [3],
5 }
6)
7df_d2 = pl.DataFrame(
8 {
9 "a": [2],
10 "d": [4],
11 }
12)
13
14df_diagonal_concat = pl.concat(
15 [
16 df_d1,
17 df_d2,
18 ],
19 how="diagonal",
20)
21print(df_diagonal_concat)
1shape: (2, 3)
2┌─────┬──────┬──────┐
3│ a ┆ b ┆ d │
4│ --- ┆ --- ┆ --- │
5│ i64 ┆ i64 ┆ i64 │
6╞═════╪══════╪══════╡
7│ 1 ┆ 3 ┆ null │
8│ 2 ┆ null ┆ 4 │
9└─────┴──────┴──────┘
rechunk
我们来说下rechunk
参数, 默认为False
, 下面有几点来阐述这个是干嘛的
-
数据存储方式: Polars中, DataFrame的每一列数据可能被存储在一个或多个独立的"数据块"中.
可以想象成, 一列很长的数据不是一次性存放在一个大块里, 而是分成好几个小块存放
-
拼接时的默认行为: 新版本下, Polars不会讲生成的新的DataFrame的列 合并为一个连续的新的数据块, 只是简单的拼接
-
好处: 速度更快, 内存占用更少
-
潜在问题: 后续操作可能会变慢, 因为数据被分成很多小的, 不连续的块儿.
那么在进行一些需要连续内存访问的操作(比如某些计算、过滤、排序等)时, 性能可能会收到影响, 因为数据不连续, 数据的局部性不好
-
重新分块什么意思: 将分散的不连续的合并到一个新的,连续的内存中, 耗费资源,因为涉及到实际的数据复制, 但后续性能会更好