Skip to content

Commit

Permalink
fix: support append/extend with null series (#11824)
Browse files Browse the repository at this point in the history
Any `Series` can now be `append`-ed or `extend`-ed with
a series of null dtype.
  • Loading branch information
tkarabela committed Nov 25, 2023
1 parent f342d5a commit ad25e20
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 3 deletions.
2 changes: 1 addition & 1 deletion crates/polars-core/src/frame/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3435,7 +3435,7 @@ fn ensure_can_extend(left: &Series, right: &Series) -> PolarsResult<()> {
left.name(), right.name(),
);
polars_ensure!(
left.dtype() == right.dtype(),
left.dtype() == right.dtype() || *right.dtype() == DataType::Null,
ShapeMismatch: "unable to vstack, dtypes for column {:?} don't match: `{}` and `{}`",
left.name(), left.dtype(), right.dtype(),
);
Expand Down
14 changes: 12 additions & 2 deletions crates/polars-core/src/series/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,12 @@ impl Series {
///
/// See [`ChunkedArray::append`] and [`ChunkedArray::extend`].
pub fn append(&mut self, other: &Series) -> PolarsResult<&mut Self> {
self._get_inner_mut().append(other)?;
if *other.dtype() == DataType::Null && *self.dtype() != DataType::Null {
let other_null = Series::full_null(other.name(), other.len(), self.dtype());
self._get_inner_mut().append(&other_null)?
} else {
self._get_inner_mut().append(other)?
}
Ok(self)
}

Expand All @@ -264,7 +269,12 @@ impl Series {
///
/// See [`ChunkedArray::extend`] and [`ChunkedArray::append`].
pub fn extend(&mut self, other: &Series) -> PolarsResult<&mut Self> {
self._get_inner_mut().extend(other)?;
if *other.dtype() == DataType::Null && *self.dtype() != DataType::Null {
let other_null = Series::full_null(other.name(), other.len(), self.dtype());
self._get_inner_mut().extend(&other_null)?
} else {
self._get_inner_mut().extend(other)?
}
Ok(self)
}

Expand Down
13 changes: 13 additions & 0 deletions py-polars/tests/unit/dataframe/test_vstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,16 @@ def test_vstack_column_name_mismatch(df1: pl.DataFrame) -> None:

with pytest.raises(pl.ShapeError):
df1.vstack(df2)


def test_vstack_with_null_column() -> None:
df1 = pl.DataFrame({"x": [3.5]}, schema={"x": pl.Float64})
df2 = pl.DataFrame({"x": [None]}, schema={"x": pl.Null})

result = df1.vstack(df2)
expected = pl.DataFrame({"x": [3.5, None]}, schema={"x": pl.Float64})

assert_frame_equal(result, expected)

with pytest.raises(pl.ShapeError):
df2.vstack(df1)
12 changes: 12 additions & 0 deletions py-polars/tests/unit/series/test_append.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,15 @@ def test_struct_schema_on_append_extend_3452() -> None:
),
):
housing1.extend(housing2)


def test_append_null_series() -> None:
a = pl.Series("a", [1, 2], pl.Int64)
b = pl.Series("b", [None, None], pl.Null)

result = a.append(b)

expected = pl.Series("a", [1, 2, None, None], pl.Int64)
assert_series_equal(a, expected)
assert_series_equal(result, expected)
assert a.n_chunks() == 2
12 changes: 12 additions & 0 deletions py-polars/tests/unit/series/test_extend.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,15 @@ def test_extend_bad_input() -> None:

with pytest.raises(AttributeError):
a.extend(b) # type: ignore[arg-type]


def test_extend_with_null_series() -> None:
a = pl.Series("a", [1, 2], pl.Int64)
b = pl.Series("b", [None, None], pl.Null)

result = a.extend(b)

expected = pl.Series("a", [1, 2, None, None], pl.Int64)
assert_series_equal(a, expected)
assert_series_equal(result, expected)
assert a.n_chunks() == 1

0 comments on commit ad25e20

Please sign in to comment.