| Signature | Description |
|---|---|
enum class gen_join_type : unsigned char { no_match = 1, // Don't include any of the join participants include_left = 2, // Include the LHS include_right = 3, // Include the RHS include_both = 4, // Include both join participants }; |
Enumerated type to specify result of a predicate for join. |
| Signature | Description | Parameters |
|---|---|---|
template<typename RHS_T, comparable LHS_COL_T, comparable RHS_COL_T, typename F, typename ... Ts> DataFrame<unsigned long> gen_join(const RHS_T &rhs, const char *lhs_col_name, const char *rhs_col_name, F &predicate) const requires std::invocable<F, const IndexType &, const typename RHS_T::IndexType &, const LHS_COL_T &, const RHS_COL_T &> && std::same_as<std::invoke_result_t<F, const IndexType &, const typename RHS_T::IndexType &, const LHS_COL_T &, const RHS_COL_T &>, gen_join_type>; |
This is the most general method to join two DataFrames. It requires the name of two columns, one from self (lhs) and one from rhs. The columns may or may not have the same type. It also takes a function called predicate. Datapoints from both self and rhs indices and the two columns are passed to predicate one by one. The returned DataFrame's index column is unsigned long. NOTE: The datapoints are passed to predicate in the same order that they are. So DataFrames' order (sorting) and predicate logic must match. NOTE: The columns are processed until the minimum length of the two columns. If you have columns of different length, you may consider calling make_consistent() before joining. NOTE: All same name columns in lhs and rhs will have lhs. and rhs. prefixes in their names in the returned DataFrame. NOTE: The result DataFrame will at least have two column names lhs.INDEX and rhs.INDEX containing the lhs and rhs indices datapoints The predicate has the following parameters:
NOTE: This join is done by what is called in the industry a table-scan |
RHS_T: Type of the rhs DataFrame LHS_COL_T: Type of the lhs column RHS_COL_T: Type of the rhs column F: Type of the predicate Ts: List all the types of all data columns. A type should be specified in the list only once. rhs: rhs DataFrame lhs_col_name: lhs (self) column name. rhs_col_name: rhs column name. predicate: A function/functor described above that determines the result |
template<typename RHS_T, comparable LHS_COL1_T, comparable RHS_COL1_T, comparable LHS_COL2_T, comparable RHS_COL2_T, typename F, typename ... Ts> DataFrame<unsigned long> gen_join(const RHS_T &rhs, const char *lhs_col1_name, const char *rhs_col1_name, const char *lhs_col2_name, const char *rhs_col2_name, F &predicate) const requires std::invocable<F, const IndexType &, const typename RHS_T::IndexType &, const LHS_COL1_T &, const RHS_COL1_T &, const LHS_COL2_T &, const RHS_COL2_T &> && std::same_as<std::invoke_result_t<F, const IndexType &, const typename RHS_T::IndexType &, const LHS_COL1_T &, const RHS_COL1_T &, const LHS_COL2_T &, const RHS_COL2_T &>, gen_join_type>; |
This is like the above gen_join but the joining logic is based on two sets of columns. The predicate has the following parameters:
|
RHS_T: Type of the rhs DataFrame LHS_COL1_T: Type of the lhs first column RHS_COL1_T: Type of the rhs first column LHS_COL2_T: Type of the lhs second column RHS_COL2_T: Type of the rhs second column F: Type of the predicate Ts: List all the types of all data columns. A type should be specified in the list only once. rhs: rhs DataFrame lhs_col1_name: lhs (self) first column name. rhs_col1_name: rhs first column name. lhs_col2_name: lhs (self) second column name. rhs_col2_name: rhs second column name. predicate: A function/functor described above that determines the result |
template<typename RHS_T, typename F, typename ... Ts> DataFrame<unsigned long> gen_join(const RHS_T &rhs, F &predicate) const requires std::invocable<F, const IndexType &, const typename RHS_T::IndexType &> && std::same_as<std::invoke_result_t<F, const IndexType &, const typename RHS_T::IndexType &>, gen_join_type>; |
This is like the above gen_join but the joining logic is only based on index columns of LHS (self) and RHS (there is no data column involved). The predicate has the following parameters:
|
RHS_T: Type of the rhs DataFrame F: Type of the predicate Ts: List all the types of all data columns. A type should be specified in the list only once. rhs: rhs DataFrame predicate: A function/functor described above that determines the result |
static void test_gen_join() { std::cout << "\nTesting gen_join( ) ..." << std::endl; std::vector<unsigned long> idx = { 123450, 123451, 123452, 123453, 123454, 123455, 123456, 123457, 123458, 123459, 123460, 123461, 123462, 123466 }; std::vector<double> d1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; std::vector<double> d2 = { 8, 9, 10, 11, 12, 13, 14, 20, 22, 23, 30, 31, 32, 1.89 }; std::vector<double> d3 = { 15, 16, 15, 18, 19, 16, 21, 0.34, 1.56, 0.34, 2.3, 0.34, 19.0 }; std::vector<int> i1 = { 22, 23, 24, 25, 99 }; ULDataFrame df; df.load_data(std::move(idx), std::make_pair("col_1", d1), std::make_pair("col_2", d2), std::make_pair("col_3", d3), std::make_pair("col_4", i1)); auto vw = df.get_view<double, int>({ "col_1", "col_2", "col_3", "col_4" }); std::vector<unsigned long> idx2 = { 123452, 123453, 123455, 123458, 123454, 223450, 223451, 223454, 223456, 123459, 223459, 223460, 223461, 123466 }; std::vector<double> d12 = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 110, 111, 112, 113, 114 }; std::vector<double> d22 = { 8, 19, 110, 111, 9, 113, 114, 99, 122, 123, 130, 131, 20, 11.89 }; std::vector<double> d32 = { 115, 116, 115, 118, 119, 116, 121, 10.34, 11.56, 10.34, 12.3, 10.34, 119.0 }; std::vector<int> i12 = { 122, 123, 124, 125, 199 }; ULDataFrame df2; df2.load_data(std::move(idx2), std::make_pair("xcol_1", d12), std::make_pair("col_2", d22), std::make_pair("xcol_3", d32), std::make_pair("col_4", i12)); auto vw2 = df2.get_view<double, int>({ "xcol_1", "col_2", "xcol_3", "col_4" }); auto predicate = [](const unsigned long &, const unsigned long &, const double &lhs_val, const double &rhs_val) -> gen_join_type { if (lhs_val == rhs_val) return (gen_join_type::include_both); return (gen_join_type::no_match); }; auto inner_result = df.gen_join<decltype(df2), double, double, decltype(predicate), double, int>(df2, "col_2", "col_2", predicate); auto inner_result_vw = vw.gen_join<decltype(df2), double, double, decltype(predicate), double, int>(df2, "col_2", "col_2", predicate); assert(inner_result.get_index().size() == 1); assert(inner_result.get_column<double>("xcol_1")[0] == 11.0); assert(inner_result.get_column<double>("xcol_3")[0] == 115.0); assert(inner_result.get_column<int>("lhs.col_4")[0] == 22); assert(inner_result.get_column<unsigned long>("rhs.INDEX")[0] == 123452); assert(inner_result.get_column<unsigned long>("lhs.INDEX")[0] == 123450); assert(inner_result_vw.get_index().size() == 1); assert(inner_result_vw.get_column<double>("col_1")[0] == 1.0); assert(inner_result_vw.get_column<int>("lhs.col_4")[0] == 22); assert(inner_result_vw.get_column<unsigned long>("rhs.INDEX")[0] == 123452); auto predicate2 = [](const unsigned long &, const unsigned long &, const double &lhs_val, const double &rhs_val) -> gen_join_type { if (lhs_val == rhs_val) return (gen_join_type::include_both); return (gen_join_type::include_right); }; auto result_vw2 = vw.gen_join<decltype(df2), double, double, decltype(predicate2), double, int>(df2, "col_2", "col_2", predicate2); assert(result_vw2.get_index().size() == 14); assert(result_vw2.get_column<double>("xcol_1")[0] == 11.0); assert(result_vw2.get_column<double>("xcol_1")[7] == 18.0); assert(result_vw2.get_column<double>("xcol_1")[13] == 114.0); assert(result_vw2.get_column<double>("xcol_3")[0] == 115.0); assert(result_vw2.get_column<double>("xcol_3")[10] == 12.3); assert(result_vw2.get_column<int>("lhs.col_4")[0] == 22); assert(result_vw2.get_column<int>("lhs.col_4")[6] == 0); assert(result_vw2.get_column<int>("lhs.col_4")[12] == 0); assert(result_vw2.get_column<int>("rhs.col_4")[0] == 122); assert(result_vw2.get_column<int>("rhs.col_4")[6] == 0); assert(result_vw2.get_column<int>("rhs.col_4")[12] == 0); assert(result_vw2.get_column<unsigned long>("rhs.INDEX")[0] == 123452); assert(result_vw2.get_column<unsigned long>("lhs.INDEX")[0] == 123450); assert(result_vw2.get_column<unsigned long>("lhs.INDEX")[8] == 0 ); auto predicate3 = [](const unsigned long &, const unsigned long &, const int &col_4, const double &xcol_1) -> gen_join_type { if ((col_4 < 23 && col_4 != 0) || xcol_1 > 112.0) return (gen_join_type::include_both); return (gen_join_type::no_match); }; auto result_vw3 = vw.gen_join<ULDataFrame, int, double, decltype(predicate3), double, int>(df2, "col_4", "xcol_1", predicate3); assert(result_vw3.get_index().size() == 3); assert(result_vw3.get_column<double>("xcol_1")[0] == 11.0); assert(result_vw3.get_column<double>("xcol_1")[1] == 113.0); assert(result_vw3.get_column<double>("xcol_1")[2] == 114.0); assert(result_vw3.get_column<unsigned long>("lhs.INDEX")[0] == 123450); assert(result_vw3.get_column<unsigned long>("lhs.INDEX")[1] == 123462); assert(result_vw3.get_column<unsigned long>("lhs.INDEX")[2] == 123466); assert(result_vw3.get_column<unsigned long>("rhs.INDEX")[0] == 123452); assert(result_vw3.get_column<unsigned long>("rhs.INDEX")[1] == 223461); assert(result_vw3.get_column<unsigned long>("rhs.INDEX")[2] == 123466); assert(result_vw3.get_column<int>("lhs.col_4")[0] == 22); assert(result_vw3.get_column<int>("lhs.col_4")[1] == 0); assert(result_vw3.get_column<int>("lhs.col_4")[2] == 0); assert(result_vw3.get_column<int>("rhs.col_4")[0] == 122); assert(result_vw3.get_column<int>("rhs.col_4")[1] == 0); assert(result_vw3.get_column<int>("rhs.col_4")[2] == 0); assert(result_vw3.get_column<double>("lhs.col_2")[0] == 8.0); assert(result_vw3.get_column<double>("lhs.col_2")[1] == 32.0); assert(result_vw3.get_column<double>("lhs.col_2")[2] == 1.89); assert(result_vw3.get_column<double>("rhs.col_2")[0] == 8.0); assert(result_vw3.get_column<double>("rhs.col_2")[1] == 20.0); assert(result_vw3.get_column<double>("rhs.col_2")[2] == 11.89); assert(result_vw3.get_column<double>("col_3")[0] == 15.0); assert(result_vw3.get_column<double>("col_3")[1] == 19.0); assert(std::isnan(result_vw3.get_column<double>("col_3")[2])); assert(result_vw3.get_column<double>("xcol_3")[0] == 115.0); assert(result_vw3.get_column<double>("xcol_3")[1] == 119.0); assert(std::isnan(result_vw3.get_column<double>("xcol_3")[2])); // Now join only by index // auto pred_by_idx = [](const unsigned long &lhs_idx, const unsigned long &rhs_idx) -> gen_join_type { if (lhs_idx == rhs_idx) return (gen_join_type::include_both); return (gen_join_type::no_match); }; auto res_by_idx = df.gen_join<ULDataFrame, decltype(pred_by_idx), double, int>(df2, pred_by_idx); assert(res_by_idx.get_index().size() == 3); assert(res_by_idx.get_column<double>("xcol_1")[0] == 15.0); assert(res_by_idx.get_column<double>("xcol_1")[1] == 110.0); assert(res_by_idx.get_column<double>("xcol_1")[2] == 114.0); assert(res_by_idx.get_column<double>("xcol_3")[0] == 119.0); assert(res_by_idx.get_column<double>("xcol_3")[1] == 10.34); assert(std::isnan(res_by_idx.get_column<double>("xcol_3")[2])); assert(res_by_idx.get_column<unsigned long>("lhs.INDEX")[0] == 123454); assert(res_by_idx.get_column<unsigned long>("lhs.INDEX")[1] == 123459); assert(res_by_idx.get_column<unsigned long>("lhs.INDEX")[2] == 123466); assert(res_by_idx.get_column<unsigned long>("rhs.INDEX")[0] == 123454); assert(res_by_idx.get_column<unsigned long>("rhs.INDEX")[1] == 123459); assert(res_by_idx.get_column<unsigned long>("rhs.INDEX")[2] == 123466); assert(res_by_idx.get_column<int>("lhs.col_4")[0] == 99); assert(res_by_idx.get_column<int>("lhs.col_4")[1] == 0); assert(res_by_idx.get_column<int>("lhs.col_4")[2] == 0); assert(res_by_idx.get_column<double>("rhs.col_2")[0] == 9.0); assert(res_by_idx.get_column<double>("rhs.col_2")[1] == 123.0); assert(res_by_idx.get_column<double>("rhs.col_2")[2] == 11.89); assert(res_by_idx.get_column<double>("lhs.col_2")[0] == 12.0); assert(res_by_idx.get_column<double>("lhs.col_2")[1] == 23.0); assert(res_by_idx.get_column<double>("lhs.col_2")[2] == 1.89); }
// ----------------------------------------------------------------------------- static void test_gen_join2() { std::cout << "\nTesting gen_join( ) two columns ..." << std::endl; // Reuse the same frames as test_gen_join() so the two tests // are easy to compare side-by-side. // std::vector<unsigned long> idx = { 123450, 123451, 123452, 123453, 123454, 123455, 123456, 123457, 123458, 123459, 123460, 123461, 123462, 123466 }; std::vector<double> d1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; std::vector<double> d2 = { 8, 9, 10, 11, 12, 13, 14, 20, 22, 23, 30, 31, 32, 1.89 }; std::vector<double> d3 = { 15, 16, 15, 18, 19, 16, 21, 0.34, 1.56, 0.34, 2.3, 0.34, 19.0 }; std::vector<int> i1 = { 22, 23, 24, 25, 99 }; ULDataFrame df; df.load_data(std::move(idx), std::make_pair("col_1", d1), std::make_pair("col_2", d2), std::make_pair("col_3", d3), std::make_pair("col_4", i1)); std::vector<unsigned long> idx2 = { 123452, 123453, 123455, 123458, 123454, 223450, 223451, 223454, 223456, 123459, 223459, 223460, 223461, 123466 }; std::vector<double> d12 = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 110, 111, 112, 113, 114 }; std::vector<double> d22 = { 8, 19, 110, 111, 9, 113, 114, 99, 122, 123, 130, 131, 20, 11.89 }; std::vector<double> d32 = { 115, 116, 115, 118, 119, 116, 121, 10.34, 11.56, 10.34, 12.3, 10.34, 119.0 }; std::vector<int> i12 = { 122, 123, 124, 125, 199 }; ULDataFrame df2; df2.load_data(std::move(idx2), std::make_pair("xcol_1", d12), std::make_pair("col_2", d22), std::make_pair("xcol_3", d32), std::make_pair("col_4", i12)); // Predicate 1 — pure inner join. // Both column pairs must satisfy their condition simultaneously: // col_2 (lhs) == col_2 (rhs) AND col_1 (lhs) < 10 // Only position 0 qualifies (lhs col_2=8 == rhs col_2=8, lhs col_1=1<10). // auto pred_1 = [](const unsigned long &, const unsigned long &, const double &lhs_col2, const double &rhs_col2, const double &lhs_col1, const double &) -> gen_join_type { if (lhs_col2 == rhs_col2 && lhs_col1 < 10.0) return (gen_join_type::include_both); return (gen_join_type::no_match); }; auto result_1 { df.gen_join<decltype(df2), double, double, // lhs_col2, rhs_col2 types double, double, // lhs_col1, rhs_col1 types decltype(pred_1), double, int>(df2, "col_2", "col_2", "col_1", "xcol_1", pred_1) }; // col_2 is the same name on both sides -> lhs.col_2 / rhs.col_2 // col_1 only exists on lhs, xcol_1 only on rhs -> no prefix needed // assert(result_1.get_index().size() == 1); assert(result_1.get_column<unsigned long>("lhs.INDEX")[0] == 123450UL); assert(result_1.get_column<unsigned long>("rhs.INDEX")[0] == 123452UL); assert(result_1.get_column<double>("col_1")[0] == 1.0); assert(result_1.get_column<double>("xcol_1")[0] == 11.0); assert(result_1.get_column<double>("lhs.col_2")[0] == 8.0); assert(result_1.get_column<double>("rhs.col_2")[0] == 8.0); assert(result_1.get_column<double>("xcol_3")[0] == 115.0); assert(result_1.get_column<double>("col_3")[0] == 15.0); // Predicate 2 — three distinct outcomes. // include_both when lhs col_2 == rhs col_2 (position 0 only) // include_left when lhs col_1 < rhs xcol_1 (positions 1-13, always true // because d1 tops out at 14 and d12 starts at 11 with d12[0] // already > d1[0]; positions 1+ all have d1<d12) // include_right never fires here (lhs col_1 is always < rhs xcol_1 when // the col_2 condition doesn't hold), so we get 14 rows: // 1 both + 13 left. // auto pred_2 = [](const unsigned long &, const unsigned long &, const double &lhs_col2, const double &rhs_col2, const double &lhs_col1, const double &rhs_xcol1) -> gen_join_type { if (lhs_col2 == rhs_col2) return (gen_join_type::include_both); if (lhs_col1 < rhs_xcol1) return (gen_join_type::include_left); return (gen_join_type::include_right); }; auto result_2 { df.gen_join<decltype(df2), double, double, // lhs_col2, rhs_col2 types double, double, // lhs_col1, rhs_xcol1 types decltype(pred_2), double, int>(df2, "col_2", "col_2", "col_1", "xcol_1", pred_2) }; assert(result_2.get_index().size() == 14); // row 0: include_both (lhs col_2[0]=8 == rhs col_2[0]=8) // assert(result_2.get_column<unsigned long>("lhs.INDEX")[0] == 123450UL); assert(result_2.get_column<unsigned long>("rhs.INDEX")[0] == 123452UL); assert(result_2.get_column<double>("col_1")[0] == 1.0); assert(result_2.get_column<double>("xcol_1")[0] == 11.0); assert(result_2.get_column<double>("lhs.col_2")[0] == 8.0); assert(result_2.get_column<double>("rhs.col_2")[0] == 8.0); // row 1: include_left (lhs col_1[1]=2 < rhs xcol_1[1]=12, col_2 mismatch) // assert(result_2.get_column<unsigned long>("lhs.INDEX")[1] == 123451UL); assert(result_2.get_column<unsigned long>("rhs.INDEX")[1] == 0UL); assert(result_2.get_column<double>("col_1")[1] == 2.0); assert(std::isnan(result_2.get_column<double>("xcol_1")[1])); assert(result_2.get_column<double>("lhs.col_2")[1] == 9.0); assert(std::isnan(result_2.get_column<double>("rhs.col_2")[1])); // row 7: include_left — verify mid-sequence // assert(result_2.get_column<unsigned long>("lhs.INDEX")[7] == 123457UL); assert(result_2.get_column<unsigned long>("rhs.INDEX")[7] == 0UL); assert(result_2.get_column<double>("col_1")[7] == 8.0); assert(std::isnan(result_2.get_column<double>("xcol_1")[7])); assert(result_2.get_column<double>("lhs.col_2")[7] == 20.0); assert(std::isnan(result_2.get_column<double>("rhs.col_2")[7])); // row 13: include_left — last row // assert(result_2.get_column<unsigned long>("lhs.INDEX")[13] == 123466UL); assert(result_2.get_column<unsigned long>("rhs.INDEX")[13] == 0UL); assert(result_2.get_column<double>("col_1")[13] == 14.0); assert(std::isnan(result_2.get_column<double>("xcol_1")[13])); assert(result_2.get_column<double>("lhs.col_2")[13] == 1.89); assert(std::isnan(result_2.get_column<double>("rhs.col_2")[13])); // Columns not involved in the predicate are still carried through // by join_helper_common_: col_3 (lhs-only), xcol_3 (rhs-only), // and col_4 (present in both -> lhs.col_4 / rhs.col_4). // assert(result_2.get_column<double>("col_3")[0] == 15.0); assert(result_2.get_column<double>("col_3")[1] == 16.0); assert(std::isnan(result_2.get_column<double>("xcol_3")[1])); assert(result_2.get_column<int>("lhs.col_4")[0] == 22); assert(result_2.get_column<int>("lhs.col_4")[1] == 23); assert(result_2.get_column<int>("rhs.col_4")[0] == 122); assert(result_2.get_column<int>("rhs.col_4")[1] == 0); }