Grouper#

class pandas::Grouper#

pandas C++ class.

Example#

#include <pandas/pandas.h>
using namespace pandas;

// Use Grouper
Grouper obj;
// ... operations ...

Constructors#

Signature

Location

Example

Grouper() = default

pd_grouper.h:32

View

explicit Grouper(const std::string& key)

pd_grouper.h:35

View

explicit Grouper( std::optional<std::string> key, std::optional<std::variant<int, std::string>> level, std::optional<std::string> freq = std::nullopt, int axis = 0, bool sort = false, std::optional<std::string> closed = std::nullopt, std::optional<std::string> label = std::nullopt, std::optional<std::string> convention = std::nullopt, const std::string& origin = "start_day", std::optional<std::string> offset = std::nullopt, bool dropna = true)

pd_grouper.h:39

View

Construction#

Signature

Return Type

Location

Example

static Grouper from_freq(const std::string& f)

static Grouper

pd_grouper.h:88

View

Data Manipulation#

Signature

Return Type

Location

Example

bool dropna() const

bool

pd_grouper.h:66

View

Grouper& set_axis(int axis)

Grouper&

pd_grouper.h:99

View

Comparison#

Signature

Return Type

Location

Example

std::optional<std::variant<int, std::string>> level() const

std::optional<std::variant<int, std::string>>

pd_grouper.h:57

View

Type Checking#

Signature

Return Type

Location

Example

bool is_time_grouper() const

bool

pd_grouper.h:69

View

Other Methods#

Signature

Return Type

Location

Example

int axis() const

int

pd_grouper.h:59

View

std::optional<std::string> closed() const

std::optional<std::string>

pd_grouper.h:61

View

std::optional<std::string> convention() const

std::optional<std::string>

pd_grouper.h:63

View

std::optional<std::string> freq() const

std::optional<std::string>

pd_grouper.h:58

View

static Grouper freq_key(const std::string& freq, const std::string& key)

static Grouper

pd_grouper.h:79

View

std::optional<std::string> key() const

std::optional<std::string>

pd_grouper.h:56

View

std::optional<std::string> label() const

std::optional<std::string>

pd_grouper.h:62

View

std::optional<std::string> offset() const

std::optional<std::string>

pd_grouper.h:65

View

std::string origin() const

std::string

pd_grouper.h:64

View

Grouper& set_closed(const std::string& closed)

Grouper&

pd_grouper.h:101

View

Grouper& set_convention(const std::string& convention)

Grouper&

pd_grouper.h:103

Grouper& set_dropna(bool dropna)

Grouper&

pd_grouper.h:106

View

Grouper& set_freq(const std::string& freq)

Grouper&

pd_grouper.h:98

View

Grouper& set_key(const std::string& key)

Grouper&

pd_grouper.h:95

View

Grouper& set_label(const std::string& label)

Grouper&

pd_grouper.h:102

Grouper& set_level(int level)

Grouper&

pd_grouper.h:96

View

Grouper& set_level(const std::string& level)

Grouper&

pd_grouper.h:97

View

Grouper& set_offset(const std::string& offset)

Grouper&

pd_grouper.h:105

Grouper& set_origin(const std::string& origin)

Grouper&

pd_grouper.h:104

Grouper& set_sort(bool sort)

Grouper&

pd_grouper.h:100

View

bool sort() const

bool

pd_grouper.h:60

View

Code Examples#

The following examples are extracted from the test suite.

Grouper (pd_test_4_all.cpp:5041)
5031        "0     True          1.1       10.0      1.0       x\n"
5032        "1    False          2.2       20.0      2.0       y\n"
5033        "2     True          3.3       30.0      3.0       z\n"
5034        "3      NaN          NaN        NaN      NaN     NaN\n"
5035        "4      NaN          NaN        NaN      NaN     NaN";
5036
5037    check_str("reindex_types_default_nan", expected, re.to_string());
5038}
5039
5040// ============================================================================
5041// Group C — `__freq_idx__` synthetic name leak in groupby(Grouper(freq=...))
5042// Source: pandasPython_tests/test_anal_grouper_freq_level_compare_full.py
5043//
5044// All 7 cases share the same shape: DatetimeIndex (quarter-end) + 2 int
5045// columns. PandasCore's groupby leaves the synthetic index name
5046// "__freq_idx__" on the result, and the formatter dutifully prints it.
5047// pandas hides this entirely. We reproduce by constructing the *result*
5048// DataFrame directly with the leaky index name — fix paths: either groupby
5049// must clear the name, or the formatter must blacklist names beginning with
5050// "__freq_idx__" / leading underscores from synthetic groupers.
5051// ============================================================================
Grouper (pd_test_4_all.cpp:5041)
5031        "0     True          1.1       10.0      1.0       x\n"
5032        "1    False          2.2       20.0      2.0       y\n"
5033        "2     True          3.3       30.0      3.0       z\n"
5034        "3      NaN          NaN        NaN      NaN     NaN\n"
5035        "4      NaN          NaN        NaN      NaN     NaN";
5036
5037    check_str("reindex_types_default_nan", expected, re.to_string());
5038}
5039
5040// ============================================================================
5041// Group C — `__freq_idx__` synthetic name leak in groupby(Grouper(freq=...))
5042// Source: pandasPython_tests/test_anal_grouper_freq_level_compare_full.py
5043//
5044// All 7 cases share the same shape: DatetimeIndex (quarter-end) + 2 int
5045// columns. PandasCore's groupby leaves the synthetic index name
5046// "__freq_idx__" on the result, and the formatter dutifully prints it.
5047// pandas hides this entirely. We reproduce by constructing the *result*
5048// DataFrame directly with the leaky index name — fix paths: either groupby
5049// must clear the name, or the formatter must blacklist names beginning with
5050// "__freq_idx__" / leading underscores from synthetic groupers.
5051// ============================================================================
Grouper (pd_test_4_all.cpp:5041)
5031        "0     True          1.1       10.0      1.0       x\n"
5032        "1    False          2.2       20.0      2.0       y\n"
5033        "2     True          3.3       30.0      3.0       z\n"
5034        "3      NaN          NaN        NaN      NaN     NaN\n"
5035        "4      NaN          NaN        NaN      NaN     NaN";
5036
5037    check_str("reindex_types_default_nan", expected, re.to_string());
5038}
5039
5040// ============================================================================
5041// Group C — `__freq_idx__` synthetic name leak in groupby(Grouper(freq=...))
5042// Source: pandasPython_tests/test_anal_grouper_freq_level_compare_full.py
5043//
5044// All 7 cases share the same shape: DatetimeIndex (quarter-end) + 2 int
5045// columns. PandasCore's groupby leaves the synthetic index name
5046// "__freq_idx__" on the result, and the formatter dutifully prints it.
5047// pandas hides this entirely. We reproduce by constructing the *result*
5048// DataFrame directly with the leaky index name — fix paths: either groupby
5049// must clear the name, or the formatter must blacklist names beginning with
5050// "__freq_idx__" / leading underscores from synthetic groupers.
5051// ============================================================================
from_freq (pd_test_5_all.cpp:352)
342    int local_fail = 0;
343    // 12 monthly dates starting 2020-01-01
344    pandas::DatetimeIndex dt_idx("2020-01-01", 12, "ME");
345    pandas::DataFrame df;
346    df.set_index(dt_idx);
347    std::vector<int64_t> amounts = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200};
348    std::vector<int64_t> counts(12, 1);
349    df.add_column<int64_t>("Amount", amounts);
350    df.add_column<int64_t>("Count", counts);
351
352    auto result = df.groupby(pandas::Grouper::from_freq("QE")).sum();
353
354    std::string expected =
355        "            Amount  Count\n"
356        "2020-03-31     600      3\n"
357        "2020-06-30    1500      3\n"
358        "2020-09-30    2400      3\n"
359        "2020-12-31    3300      3";
360    pandas_tests::check(result.to_string() == expected, "grouper_freq_dtindex.str", local_fail);
361    if (local_fail > 0) {
362        std::cout << "  [FAIL] : in f_test_anal_grouper_freq_level_compare_full_175() : " << local_fail << " checks failed" << std::endl;
dropna (pd_test_1_all.cpp:531)
521        }
522
523        // Test isna array
524        numpy::NDArray<numpy::bool_> na_mask = arr.isna();
525        if (na_mask.getSize() != 4) {
526            std::cout << "  [FAIL] : in pd_test_categorical_array_na_handling() : isna size != 4" << std::endl;
527            throw std::runtime_error("pd_test_categorical_array_na_handling failed: isna size != 4");
528        }
529
530        // Test dropna
531        pandas::CategoricalArray dropped = arr.dropna();
532        if (dropped.size() != 2) {
533            std::cout << "  [FAIL] : in pd_test_categorical_array_na_handling() : dropna size != 2" << std::endl;
534            throw std::runtime_error("pd_test_categorical_array_na_handling failed: dropna size != 2");
535        }
536
537        // Test fillna (fill with existing category)
538        pandas::CategoricalArray filled = arr.fillna("a");  // 'a' is in categories
539        if (filled.has_na()) {
540            std::cout << "  [FAIL] : in pd_test_categorical_array_na_handling() : fillna should have no NA" << std::endl;
541            throw std::runtime_error("pd_test_categorical_array_na_handling failed: fillna should have no NA");
set_axis (pd_test_1_all.cpp:6673)
6663            std::cout << " -> tests passed" << std::endl;
6664        }
6665
6666        // =====================================================================
6667        // Test: Index Operations
6668        // =====================================================================
6669        void pd_test_dataframe_index_ops() {
6670            std::cout << "========= index operations =================";
6671
6672            // Test set_axis (rows)
6673            {
6674                std::map<std::string, std::vector<int>> data;
6675                data["A"] = {1, 2, 3};
6676                pandas::DataFrame df(data);
6677
6678                auto renamed = df.set_axis({"x", "y", "z"}, 0);
6679                std::string idx0 = renamed.index().get_value_str(0);
6680                if (idx0 != "x") {
6681                    std::cout << "  [FAIL] : in pd_test_dataframe_index_ops() : set_axis first label should be 'x'" << std::endl;
6682                    throw std::runtime_error("pd_test_dataframe_index_ops failed: set_axis");
level (pd_test_2_all.cpp:10027)
10017                std::cout << "  [FAIL] : in pd_test_swaplevel_negative_indices() : wrong index values" << std::endl;
10018                std::cout << "    Expected: C:B:A, F:E:D" << std::endl;
10019                std::cout << "    Got: " << idx0 << ", " << idx1 << std::endl;
10020                throw std::runtime_error("pd_test_swaplevel_negative_indices failed");
10021            }
10022
10023            std::cout << " -> tests passed" << std::endl;
10024        }
10025
10026        void pd_test_swaplevel_same_level() {
10027            std::cout << "========= swaplevel same level (no change) =============";
10028
10029            std::map<std::string, std::vector<std::string>> data = {
10030                {"value", {"a", "b"}}
10031            };
10032
10033            pandas::DataFrame df(data);
10034
10035            std::vector<std::string> hier_index = {"X:Y:Z", "A:B:C"};
10036            df.set_index(std::make_unique<pandas::Index<std::string>>(hier_index));
is_time_grouper (pd_test_3_all.cpp:19042)
19032        throw std::runtime_error("pd_test_grouper_builder: freq failed");
19033    }
19034    if (g.sort()) {
19035        std::cout << "  [FAIL] : builder sort failed" << std::endl;
19036        throw std::runtime_error("pd_test_grouper_builder: sort failed");
19037    }
19038    if (g.dropna()) {
19039        std::cout << "  [FAIL] : builder dropna failed" << std::endl;
19040        throw std::runtime_error("pd_test_grouper_builder: dropna failed");
19041    }
19042    if (!g.is_time_grouper()) {
19043        std::cout << "  [FAIL] : is_time_grouper failed" << std::endl;
19044        throw std::runtime_error("pd_test_grouper_builder: is_time_grouper failed");
19045    }
19046
19047    std::cout << " -> tests passed" << std::endl;
19048}
19049
19050// ============================================================================
19051// Test Grouper full constructor
19052// ============================================================================
axis (pd_test_1_all.cpp:25083)
25073            }
25074
25075            if (!passed) {
25076                throw std::runtime_error("pd_test_median_empty_column failed");
25077            }
25078
25079            std::cout << " -> tests passed" << std::endl;
25080        }
25081
25082        void pd_test_median_invalid_axis() {
25083            std::cout << "========= median invalid axis (throws exception) =================";
25084
25085            std::map<std::string, std::vector<numpy::float64>> data;
25086            data["A"] = {1.0, 2.0, 3.0};
25087
25088            pandas::DataFrame df(data);
25089
25090            bool passed = false;
25091            try {
25092                df.median(2);  // Invalid axis
25093            } catch (const std::exception&) {
closed (pd_test_1_all.cpp:1903)
1893// ============================================================================
1894void test_constructors() {
1895    std::cout << "========= IntervalArray: constructors ======================= ";
1896
1897    // Default constructor
1898    pandas::IntervalArrayFloat64 empty;
1899    if (empty.size() != 0) {
1900        std::cout << "[FAIL] : in test_constructors() : default constructor size" << std::endl;
1901        return;
1902    }
1903    if (empty.closed() != pandas::IntervalClosed::Right) {
1904        std::cout << "[FAIL] : in test_constructors() : default closure" << std::endl;
1905        return;
1906    }
1907
1908    // Constructor from left/right arrays
1909    numpy::NDArray<numpy::float64> left(std::vector<size_t>{3});
1910    numpy::NDArray<numpy::float64> right(std::vector<size_t>{3});
1911    left.setElementAt({0}, 0.0);  right.setElementAt({0}, 1.0);
1912    left.setElementAt({1}, 1.0);  right.setElementAt({1}, 2.0);
1913    left.setElementAt({2}, 2.0);  right.setElementAt({2}, 3.0);
convention (pd_test_5_all.cpp:67797)
67787    std::cout << "-- case_30_agg_var_passlock\n";
67788    auto s = make_carrier({1.0, 2.0, 3.0}, "UInt8");
67789    auto v = s.agg("var");
67790    pandas_tests::check(opt_equals(v, 1.0, "case_30.agg_var_value"),
67791          "case_30.agg_var_eq_1", local_fail);
67792}
67793
67794void f_series_uint_sum_agg_typed_9_4791556_case_31_empty_uint8_sum_value(int& local_fail) {
67795    std::cout << "-- case_31_empty_uint8_sum_value\n";
67796    // empty UInt8: sum() returns optional<double>(0.0) per pandas
67797    // convention (identity element). Binding stamps uint32(0).
67798    auto s = make_carrier({}, "UInt8");
67799    auto v = s.sum();
67800    pandas_tests::check(opt_equals(v, 0.0, "case_31.empty_sum_value"),
67801          "case_31.empty_sum_eq_0", local_fail);
67802}
67803
67804void f_series_uint_sum_agg_typed_9_4791556_case_32_empty_uint8_prod_value(int& local_fail) {
67805    std::cout << "-- case_32_empty_uint8_prod_value\n";
67806    // empty UInt8 prod -> 1 (multiplicative identity). Binding stamps
67807    // uint32(1).
freq (pd_test_1_all.cpp:8233)
8223    std::cout << "========= freq property ===============================";
8224
8225    std::vector<std::optional<numpy::datetime64>> values = {
8226        numpy::datetime64(0LL, numpy::DateTimeUnit::Nanosecond),
8227        numpy::datetime64(86400000000000LL, numpy::DateTimeUnit::Nanosecond)  // 1 day
8228    };
8229    pandas::DatetimeArray arr(values);
8230    pandas::DatetimeMixinIndex idx(arr);
8231
8232    // Default freq is nullopt or inferred
8233    auto f = idx.freq();
8234    std::string fs = idx.freqstr();
8235
8236    bool passed = true;  // freq may or may not be set
8237    if (!passed) {
8238        std::cout << "  [FAIL] : in pd_test_datetime_mixin_freq()" << std::endl;
8239        throw std::runtime_error("pd_test_datetime_mixin_freq failed");
8240    }
8241
8242    std::cout << " -> tests passed" << std::endl;
8243}
freq_key (pd_test_3_all.cpp:11083)
11073void pd_test_3_all_groupby_mixed() {
11074    std::cout << "========= groupby(mixed Grouper+string) ================";
11075    pandas::DataFrame df;
11076    df.add_column<std::string>("Buyer", {"Alice","Bob","Alice","Bob"});
11077    df.add_column<double>("Amount", {100.0, 200.0, 150.0, 250.0});
11078    auto dates = pandas::date_range("2024-01-15", 4, "15D");
11079    df.set_index(std::make_shared<pandas::DatetimeIndex>(dates));
11080    df.add_column<std::string>("Date_str", {"2024-01-15","2024-01-30","2024-02-14","2024-03-01"});
11081    auto result = df.groupby(
11082        {pandas::Grouper::freq_key("1ME", "Date_str"), std::string("Buyer")}).sum();
11083    if (result.nrows() < 2) {
11084        std::cout << "  [FAIL] : in pd_test_3_all_groupby_mixed()" << std::endl;
11085        throw std::runtime_error("groupby mixed failed");
11086    }
11087    std::cout << " -> tests passed" << std::endl;
11088}
11089
11090void pd_test_3_all_ndframebase_unstack() {
11091    std::cout << "========= NDFrameBase.unstack() ========================";
11092    auto mi = pandas::MultiIndex::from_arrays<std::string>(
key (pd_test_2_all.cpp:11898)
11888            if (!threw) {
11889                std::cout << "  [FAIL] : should have thrown" << std::endl;
11890                throw std::runtime_error("pd_test_to_hdf_invalid_mode failed");
11891            }
11892
11893            std::cout << " -> tests passed" << std::endl;
11894        }
11895
11896        void pd_test_to_hdf_empty_key() {
11897            std::cout << "========= to_hdf empty key (real HDF5) =========================";
11898
11899            std::map<std::string, std::vector<double>> data = {{"col", {1.0, 2.0, 3.0}}};
11900            pandas::DataFrame df(data);
11901
11902            bool threw = false;
11903            try {
11904                df.to_hdf("temp/test.h5", "");
11905            } catch (const std::exception& e) {
11906                threw = true;
11907                std::string msg = e.what();
label (pd_test_4_all.cpp:4935)
4925// Helper: compare and report
4926// ----------------------------------------------------------------------------
4927static void check_str(const std::string& label,
4928                      const std::string& expected,
4929                      const std::string& actual) {
4930    int _f = 0;
4931    pandas_tests::check_str_ws(label, expected, actual, _f);
4932    if (_f > 0) throw std::runtime_error(label + ": str mismatch");
4933}
4934
4935// Slugify a python compare-test label ("a.b.c" → "a_b_c") matching the
4936// scheme in scripts/gen_repr_mismatch_fixtures.py.
4937static std::string slugify_label(const std::string& label) {
4938    std::string out = label;
4939    for (char& ch : out) {
4940        if (ch == '.') ch = '_';
4941    }
4942    return out;
4943}
4944
4945// Load a captured pandas-generated expected output file. The file is written
offset (pd_test_3_all.cpp:18224)
18214namespace dataframe_tests {
18215namespace dataframe_tests_dateoffset {
18216
18217// ============================================================================
18218// Test Day offset
18219// ============================================================================
18220
18221void pd_test_day_offset() {
18222    std::cout << "========= Day offset ===================================";
18223
18224    pandas::Day offset(5);
18225    if (offset.n() != 5) {
18226        std::cout << "  [FAIL] : Day n() failed" << std::endl;
18227        throw std::runtime_error("pd_test_day_offset: n() failed");
18228    }
18229    if (offset.freqstr() != "D") {
18230        std::cout << "  [FAIL] : Day freqstr() failed" << std::endl;
18231        throw std::runtime_error("pd_test_day_offset: freqstr() failed");
18232    }
18233    if (offset.name() != "Day") {
18234        std::cout << "  [FAIL] : Day name() failed" << std::endl;
origin (pd_test_2_all.cpp:22665)
22655        pandas::Timestamp ts(v->getValue());
22656        check(ts.year() == 1970, "year==1970");
22657        check(ts.month() == 1, "month==1");
22658        check(ts.day() == 2, "day==2");
22659    }
22660}
22661
22662void test_to_datetime_numeric_origin() {
22663    std::cout << "  -- test_to_datetime_numeric_origin --" << std::endl;
22664    // origin = 2000-01-01, values = [1, 2, 3] days
22665    pandas::Timestamp origin(2000, 1, 1, 0, 0, 0, 0, 0, "");
22666    int64_t origin_ns = origin.value();
22667    std::vector<double> vals = {1.0, 2.0, 3.0};
22668    auto arr = pandas::to_datetime_numeric(vals, "D", origin_ns);
22669    check(arr.size() == 3, "size==3");
22670
22671    // Day 1 from 2000-01-01 = 2000-01-02
22672    auto v0 = arr[0];
22673    check(v0.has_value(), "v0_has_value");
22674    if (v0.has_value()) {
22675        pandas::Timestamp ts(v0->getValue());
set_closed (pd_test_1_all.cpp:2285)
2275    std::vector<numpy::float64> breaks = {0.0, 1.0, 2.0};
2276    auto arr = pandas::IntervalArrayFloat64::from_breaks(breaks, pandas::IntervalClosed::Right);
2277
2278    if (arr.closed() != pandas::IntervalClosed::Right) {
2279        std::cout << "[FAIL] : in test_set_closed() : initial closure" << std::endl;
2280        return;
2281    }
2282
2283    // Change to left-closed
2284    auto arr_left = arr.set_closed(pandas::IntervalClosed::Left);
2285    if (arr_left.closed() != pandas::IntervalClosed::Left) {
2286        std::cout << "[FAIL] : in test_set_closed() : set to Left" << std::endl;
2287        return;
2288    }
2289
2290    // Original should be unchanged
2291    if (arr.closed() != pandas::IntervalClosed::Right) {
2292        std::cout << "[FAIL] : in test_set_closed() : original changed" << std::endl;
2293        return;
2294    }
set_dropna (pd_test_3_all.cpp:19024)
19014}
19015
19016// ============================================================================
19017// Test Grouper builder pattern
19018// ============================================================================
19019
19020void pd_test_grouper_builder() {
19021    std::cout << "========= Grouper: builder pattern =========================";
19022
19023    pandas::Grouper g;
19024    g.set_key("A").set_freq("M").set_sort(false).set_dropna(false);
19025
19026    if (g.key().value() != "A") {
19027        std::cout << "  [FAIL] : builder key failed" << std::endl;
19028        throw std::runtime_error("pd_test_grouper_builder: key failed");
19029    }
19030    if (g.freq().value() != "M") {
19031        std::cout << "  [FAIL] : builder freq failed" << std::endl;
19032        throw std::runtime_error("pd_test_grouper_builder: freq failed");
19033    }
19034    if (g.sort()) {
set_freq (pd_test_1_all.cpp:8254)
8244void pd_test_datetime_mixin_set_freq() {
8245    std::cout << "========= set_freq ====================================";
8246
8247    std::vector<std::optional<numpy::datetime64>> values = {
8248        numpy::datetime64(0LL, numpy::DateTimeUnit::Nanosecond)
8249    };
8250    pandas::DatetimeArray arr(values);
8251    pandas::DatetimeMixinIndex idx(arr);
8252
8253    idx.set_freq("D");
8254    auto f = idx.freq();
8255
8256    bool passed = (f.has_value() && *f == "D");
8257    if (!passed) {
8258        std::cout << "  [FAIL] : in pd_test_datetime_mixin_set_freq()" << std::endl;
8259        throw std::runtime_error("pd_test_datetime_mixin_set_freq failed");
8260    }
8261
8262    std::cout << " -> tests passed" << std::endl;
8263}
set_key (pd_test_3_all.cpp:19024)
19014}
19015
19016// ============================================================================
19017// Test Grouper builder pattern
19018// ============================================================================
19019
19020void pd_test_grouper_builder() {
19021    std::cout << "========= Grouper: builder pattern =========================";
19022
19023    pandas::Grouper g;
19024    g.set_key("A").set_freq("M").set_sort(false).set_dropna(false);
19025
19026    if (g.key().value() != "A") {
19027        std::cout << "  [FAIL] : builder key failed" << std::endl;
19028        throw std::runtime_error("pd_test_grouper_builder: key failed");
19029    }
19030    if (g.freq().value() != "M") {
19031        std::cout << "  [FAIL] : builder freq failed" << std::endl;
19032        throw std::runtime_error("pd_test_grouper_builder: freq failed");
19033    }
19034    if (g.sort()) {
set_level (pd_test_5_all.cpp:21141)
21131                 "case7.index_name_is_date"),
21132          "case7.index_name_is_date", local_fail);
21133    pandas_tests::check(str_eq(result.to_string(), expected, "case7.to_string"),
21134          "case7.to_string_matches_pandas", local_fail);
21135}
21136
21137void f_grouper_freq_key_col_index_name_742013_case_8_named_dtindex_level0_QE(int& local_fail) {
21138    std::cout << "-- f_grouper_freq_key_col_index_name_742013_case_8_named_dtindex_level0_QE\n";
21139    auto df = make_df_named_dtindex_as_index();
21140    pandas::Grouper g;
21141    g.set_level(0).set_freq("QE");
21142    auto result = df.groupby(g).sum();
21143
21144    std::string expected =
21145        "            Amount  Count\n"
21146        "dtidx                    \n"
21147        "2020-03-31     600      3\n"
21148        "2020-06-30    1500      3\n"
21149        "2020-09-30    2400      3\n"
21150        "2020-12-31    3300      3";
21151    pandas_tests::check(opt_eq(result.index_name(), std::optional<std::string>("dtidx"),
set_level (pd_test_5_all.cpp:21141)
21131                 "case7.index_name_is_date"),
21132          "case7.index_name_is_date", local_fail);
21133    pandas_tests::check(str_eq(result.to_string(), expected, "case7.to_string"),
21134          "case7.to_string_matches_pandas", local_fail);
21135}
21136
21137void f_grouper_freq_key_col_index_name_742013_case_8_named_dtindex_level0_QE(int& local_fail) {
21138    std::cout << "-- f_grouper_freq_key_col_index_name_742013_case_8_named_dtindex_level0_QE\n";
21139    auto df = make_df_named_dtindex_as_index();
21140    pandas::Grouper g;
21141    g.set_level(0).set_freq("QE");
21142    auto result = df.groupby(g).sum();
21143
21144    std::string expected =
21145        "            Amount  Count\n"
21146        "dtidx                    \n"
21147        "2020-03-31     600      3\n"
21148        "2020-06-30    1500      3\n"
21149        "2020-09-30    2400      3\n"
21150        "2020-12-31    3300      3";
21151    pandas_tests::check(opt_eq(result.index_name(), std::optional<std::string>("dtidx"),
set_sort (pd_test_3_all.cpp:19024)
19014}
19015
19016// ============================================================================
19017// Test Grouper builder pattern
19018// ============================================================================
19019
19020void pd_test_grouper_builder() {
19021    std::cout << "========= Grouper: builder pattern =========================";
19022
19023    pandas::Grouper g;
19024    g.set_key("A").set_freq("M").set_sort(false).set_dropna(false);
19025
19026    if (g.key().value() != "A") {
19027        std::cout << "  [FAIL] : builder key failed" << std::endl;
19028        throw std::runtime_error("pd_test_grouper_builder: key failed");
19029    }
19030    if (g.freq().value() != "M") {
19031        std::cout << "  [FAIL] : builder freq failed" << std::endl;
19032        throw std::runtime_error("pd_test_grouper_builder: freq failed");
19033    }
19034    if (g.sort()) {
sort (pd_test_3_all.cpp:3869)
3859        throw std::runtime_error("last 2 positions should be NaN");
3860    }
3861    if (std::abs(result[0] - 3.0) > 0.001) {
3862        throw std::runtime_error("shift(-2) [0] should be 3.0");
3863    }
3864
3865    std::cout << " -> tests passed" << std::endl;
3866}
3867
3868void pd_test_3_all_index_sort() {
3869    std::cout << "========= Index.sort() =============================";
3870
3871    pandas::Index<numpy::int64> idx({3, 1, 4, 1, 5, 9, 2, 6});
3872    auto result = idx.sort();
3873
3874    if (result[0] != 1 || result[1] != 1 || result[7] != 9) {
3875        throw std::runtime_error("sort() not working correctly");
3876    }
3877
3878    // Test descending
3879    result = idx.sort(false);