diff --git a/docs/api/dt/median.rst b/docs/api/dt/median.rst
index 61eac12c42..b322082c85 100644
--- a/docs/api/dt/median.rst
+++ b/docs/api/dt/median.rst
@@ -14,8 +14,9 @@
Input columns.
return: Expr
- f-expression having one row, and the same names, stypes and
- number of columns as in `cols`.
+ f-expression having one row, and the same names and number of columns
+ as in `cols`. The column stypes are `float32` for
+ `float32` columns, and `float64` for all the other numeric types.
except: TypeError
The exception is raised when one of the columns from `cols`
diff --git a/docs/releases/v1.1.0.rst b/docs/releases/v1.1.0.rst
index 7346afa9d2..78e8189125 100644
--- a/docs/releases/v1.1.0.rst
+++ b/docs/releases/v1.1.0.rst
@@ -139,6 +139,9 @@
-[fix] Reducers and row-wise functions now support :attr:`void
`
columns. [#3284]
+ -[fix] Fixed :func:`dt.median()` when used in a groupby context with
+ :attr:`void ` columns. [#3411]
+
fread
-----
diff --git a/src/core/expr/head_reduce_unary.cc b/src/core/expr/head_reduce_unary.cc
index 8267be937b..9fedaa5de7 100644
--- a/src/core/expr/head_reduce_unary.cc
+++ b/src/core/expr/head_reduce_unary.cc
@@ -830,7 +830,7 @@ static Column compute_median(Column&& arg, const Groupby& gby) {
return Column::new_na_column(1, arg.stype());
}
switch (arg.stype()) {
- case SType::VOID: return Column(new ConstNa_ColumnImpl(1));
+ case SType::VOID: return Column(new ConstNa_ColumnImpl(gby.size()));
case SType::BOOL:
case SType::INT8: return _median (std::move(arg), gby);
case SType::INT16: return _median(std::move(arg), gby);
diff --git a/src/core/sort.cc b/src/core/sort.cc
index 8c763dd9b3..ef8ff90926 100644
--- a/src/core/sort.cc
+++ b/src/core/sort.cc
@@ -1497,6 +1497,7 @@ RiGb group(const std::vector& columns,
void dt::ColumnImpl::sort_grouped(const Groupby& grps, Column& out) {
+ out.materialize(); // `SortContext::continue_sort()` requires material column
(void) out.stats();
SortContext sc(nrows(), RowIndex(), grps, /* make_groups = */ false);
sc.continue_sort(out, /* desc = */ false, /* make_groups = */ false);
diff --git a/tests/test-reduce.py b/tests/test-reduce.py
index 9d9cc5da64..221d990bae 100644
--- a/tests/test-reduce.py
+++ b/tests/test-reduce.py
@@ -458,19 +458,25 @@ def test_mean_empty_frame():
def test_median_void():
DT = dt.Frame([None] * 10)
- DT_median = DT[:, mean(f.C0)]
+ DT_median = DT[:, median(f.C0)]
assert_equals(DT_median, dt.Frame([None]))
def test_median_void_per_group():
DT = dt.Frame([[None, None, None, None, None], [1, 2, 1, 2, 2]])
- DT_median = DT[:, mean(f.C0), by(f.C1)]
+ DT_median = DT[:, median(f.C0), by(f.C1)]
assert_equals(DT_median, dt.Frame(C1=[1, 2]/dt.int32, C0=[None, None]))
+def test_median_nonvoid_per_void_group():
+ DT = dt.Frame([[None, None, None, None, None], [1, 2, 1, 2, 2]])
+ DT_median = DT[:, median(f.C1), by(f.C0)]
+ assert_equals(DT_median, dt.Frame([[None], [2]/dt.float64]))
+
+
def test_median_void_grouped():
DT = dt.Frame([[None, None, None, None, None], [1, 2, 1, 2, 2]])
- DT_median = DT[:, mean(f.C0), by(f.C0)]
+ DT_median = DT[:, median(f.C0), by(f.C0)]
assert_equals(DT_median, dt.Frame([[None], [None]/dt.float64]))