跳到内容

拼接

有多种方法可以将来自不同 DataFrame 的数据进行连接

  • 两个具有相同列的 DataFrame 可以垂直连接,以生成一个更长的 DataFrame
  • 两个具有不重叠列的 DataFrame 可以水平连接,以生成一个更宽的 DataFrame
  • 两个具有不同行数和列数的 DataFrame 可以对角连接,以生成一个可能更长和/或更宽的 DataFrame。如果列名重叠,值将垂直连接。如果列名不重叠,将添加新的行和列。缺失值将设置为 null

垂直连接 - 变长

在垂直连接中,您将来自 DataFrames 列表中的所有行组合成一个更长的 DataFrame

concat

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)

concat

let df_v1 = df!(
        "a"=> &[1],
        "b"=> &[3],
)?;
let df_v2 = df!(
        "a"=> &[2],
        "b"=> &[4],
)?;
let df_vertical_concat = concat(
    [df_v1.clone().lazy(), df_v2.clone().lazy()],
    UnionArgs::default(),
)?
.collect()?;
println!("{}", &df_vertical_concat);

shape: (2, 2)
┌─────┬─────┐
│ a   ┆ b   │
│ --- ┆ --- │
│ i64 ┆ i64 │
╞═════╪═════╡
│ 1   ┆ 3   │
│ 2   ┆ 4   │
└─────┴─────┘

当 DataFrame 不具有相同列名时,垂直连接会失败。

水平连接 - 变宽

在水平连接中,您将来自 DataFrames 列表中的所有列组合成一个更宽的 DataFrame

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)

concat

let df_h1 = df!(
        "l1"=> &[1, 2],
        "l2"=> &[3, 4],
)?;
let df_h2 = df!(
        "r1"=> &[5, 6],
        "r2"=> &[7, 8],
        "r3"=> &[9, 10],
)?;
let df_horizontal_concat = polars::functions::concat_df_horizontal(&[df_h1, df_h2], true)?;
println!("{}", &df_horizontal_concat);

shape: (2, 5)
┌─────┬─────┬─────┬─────┬─────┐
│ l1  ┆ l2  ┆ r1  ┆ r2  ┆ r3  │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 │
╞═════╪═════╪═════╪═════╪═════╡
│ 1   ┆ 3   ┆ 5   ┆ 7   ┆ 9   │
│ 2   ┆ 4   ┆ 6   ┆ 8   ┆ 10  │
└─────┴─────┴─────┴─────┴─────┘

当 DataFrame 具有重叠列时,水平连接会失败。

当 DataFrame 具有不同行数时,列的末尾将用 null 值填充到最大长度。

concat

df_h1 = pl.DataFrame(
    {
        "l1": [1, 2],
        "l2": [3, 4],
    }
)
df_h2 = pl.DataFrame(
    {
        "r1": [5, 6, 7],
        "r2": [8, 9, 10],
    }
)
df_horizontal_concat = pl.concat(
    [
        df_h1,
        df_h2,
    ],
    how="horizontal",
)
print(df_horizontal_concat)

concat

let df_h1 = df!(
        "l1"=> &[1, 2],
        "l2"=> &[3, 4],
)?;
let df_h2 = df!(
        "r1"=> &[5, 6, 7],
        "r2"=> &[8, 9, 10],
)?;
let df_horizontal_concat = polars::functions::concat_df_horizontal(&[df_h1, df_h2], true)?;
println!("{}", &df_horizontal_concat);

shape: (3, 4)
┌──────┬──────┬─────┬─────┐
│ l1   ┆ l2   ┆ r1  ┆ r2  │
│ ---  ┆ ---  ┆ --- ┆ --- │
│ i64  ┆ i64  ┆ i64 ┆ i64 │
╞══════╪══════╪═════╪═════╡
│ 1    ┆ 3    ┆ 5   ┆ 8   │
│ 2    ┆ 4    ┆ 6   ┆ 9   │
│ null ┆ null ┆ 7   ┆ 10  │
└──────┴──────┴─────┴─────┘

对角连接 - 变得更长、更宽且含更多 null

在对角连接中,您将来自 DataFrames 列表中的所有行和列组合成一个更长和/或更宽的 DataFrame

concat

df_d1 = pl.DataFrame(
    {
        "a": [1],
        "b": [3],
    }
)
df_d2 = pl.DataFrame(
    {
        "a": [2],
        "d": [4],
    }
)

df_diagonal_concat = pl.concat(
    [
        df_d1,
        df_d2,
    ],
    how="diagonal",
)
print(df_diagonal_concat)

concat

let df_d1 = df!(
    "a"=> &[1],
    "b"=> &[3],
)?;
let df_d2 = df!(
        "a"=> &[2],
        "d"=> &[4],)?;
let df_diagonal_concat = polars::functions::concat_df_diagonal(&[df_d1, df_d2])?;
println!("{}", &df_diagonal_concat);

shape: (2, 3)
┌─────┬──────┬──────┐
│ a   ┆ b    ┆ d    │
│ --- ┆ ---  ┆ ---  │
│ i64 ┆ i64  ┆ i64  │
╞═════╪══════╪══════╡
│ 1   ┆ 3    ┆ null │
│ 2   ┆ null ┆ 4    │
└─────┴──────┴──────┘

当列名不重叠时,对角连接会生成 null 值。

当 DataFrame 形状不匹配并且我们有一个重叠的语义键时,我们可以连接这些 DataFrame,而不是拼接它们。

重新分块

在连接之前,我们有两个 DataFrame df1df2df1df2 中的每个列都存储在一个或多个内存块中。默认情况下,在连接过程中,每个列中的块不会被连续化。这使得连接操作更快,消耗更少内存,但可能会减慢未来受益于数据连续内存存储的操作。将碎片化的块复制到一个新的单一块中的过程称为重新分块。重新分块是一个开销很大的操作。在 0.20.26 版本之前,默认是执行重新分块,但在新版本中,默认不执行。如果您希望 Polars 重新分块连接后的 DataFrame,您可以在连接时指定 rechunk = True