Home » Custom Grouping! Part 15

Custom Grouping! Part 15

Solving Custom Grouping Part 15 challenge by Power Query, Power BI, Excel, Python and R

The Question table contains transactions recorded on different dates. For each month with n transactions, group them as follows: Transaction 1 and n should belong to the same group. Transaction 2 and n-1 should form another group. Transaction 3 and n-2 should be grouped together. Continue this pattern until all transactions are grouped accordingly.

📌 Challenge Details and Links
Challenge Number: 173
Challenge Difficulty: ⭐⭐⭐⭐
📥Download Sample File
📥Link to the solutions on LinkedIn

Solving the challenge of Custom Grouping! Part 15 with Power Query

Power Query solution 1 for Custom Grouping! Part 15, proposed by Alejandro Simón 🇵🇦 🇪🇸:
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
Sol = Table.Combine(Table.Group(Source, "Date", {{"A", (x)=> 
 let
 a = x,
 b = Table.RowCount(a),
 c = if Number.IsEven(b) then {1..b/2}&List.Reverse({1..b/2}) 
 else {1..(b/2+.5)}&List.Skip(List.Reverse({1..(b/2+.5)})),
 e = List.Zip({List.Repeat({Date.Month(x[Date]{0})},b), c}),
 f = List.Transform(e, each Text.From(_{0})&"-"&Text.From(_{1})),
 g = Table.FromColumns(Table.ToColumns(a)&{f}, Table.ColumnNames(a)&{"Group"})
 in g}},
0, (x,y)=> Number.From(Date.Month(x)<>Date.Month(y)))[A])
in
Sol
Power Query solution 2 for Custom Grouping! Part 15, proposed by Kris Jaganah:
let 
 a = Table.AddIndexColumn( _ ,"Id",1) in 
 Table.AddColumn(a, "Group",(v)=> 
 Text.From(Date.Month(v[Date])) &"-"& Text.From( 
 if v[Id] > List.Max(a[Id])/2 then List.Max(a[Id])+1 -v[Id] else v[Id] ) )
 [[Date],[Quantity],[Group]] },
 0,(x,y)=> Number.From( Date.Month(x) <> Date.Month(y) ))[All])
Power Query solution 3 for Custom Grouping! Part 15, proposed by CA Raghunath Gundi:
let
  Source = Excel.CurrentWorkbook(){[Name = "Table1"]}[Content], 
  Grp = Table.Group(
    Source, 
    {"Date"}, 
    {{"Grp", each _}}, 
    0, 
    (x, y) => Value.Compare(Date.Month(y[Date]), Date.Month(x[Date]))
  ), 
  Index1 = Table.AddIndexColumn(Grp, "Index1", 1, 1, Int64.Type), 
  Logic = Table.TransformColumns(
    Index1, 
    {
      "Grp", 
      each [
        a = Table.AddIndexColumn(_, "Index2", 1, 1), 
        b = Table.RowCount(a), 
        c = Table.AddIndexColumn(a, "Index3", b, - 1), 
        d = Table.AddColumn(c, "Final", each if [Index2] > b / 2 then [Index3] else [Index2])
      ][d][[Date], [Quantity], [Final]]
    }
  ), 
  Expand = Table.ExpandTableColumn(Logic, "Grp", {"Quantity", "Final"}, {"Quantity", "Final"}), 
  Merge = Table.CombineColumns(
    Table.TransformColumnTypes(Expand, {{"Index1", type text}, {"Final", type text}}, "en-IN"), 
    {"Index1", "Final"}, 
    Combiner.CombineTextByDelimiter("-", QuoteStyle.None), 
    "Group"
  )
in
  Merge
Power Query solution 4 for Custom Grouping! Part 15, proposed by Seokho MOON:
let
  Source = Excel.CurrentWorkbook(){[Name = "Table1"]}[Content], 
  Group = Table.Group(
    Source, 
    "Date", 
    {"Tbl", F}, 
    0, 
    (x, y) => Value.Compare(Date.Month(x), Date.Month(y))
  )[[Tbl]], 
  F = each [
    A = Table.AddIndexColumn(_, "Temp", 1), 
    B = Table.AddColumn(A, "Idx_2", each List.Min({[Temp], Table.RowCount(A) - [Temp] + 1}))
  ][B], 
  AddIdx = Table.AddIndexColumn(Group, "Idx_1", 1), 
  Expand = Table.ExpandTableColumn(AddIdx, "Tbl", {"Date", "Quantity", "Idx_2"}), 
  Res = Table.AddColumn(Expand, "Group", each Text.From([Idx_1]) & "-" & Text.From([Idx_2]))[
    [Date], 
    [Quantity], 
    [Group]
  ]
in
  Res
Power Query solution 5 for Custom Grouping! Part 15, proposed by Mihai Radu O:
let
  Source = Excel.CurrentWorkbook(){[Name = "Table4"]}[Content], 
  tip = Table.TransformColumnTypes(Source, {{"Date", type date}}), 
  s = [
    a = tip[Date], 
    b = List.Generate(
      () => [x = 0, y = 1], 
      each [x] < List.Count(a), 
      each [x = [x] + 1, y = [y] + Byte.From(Date.Month(a{x}) <> Date.Month(a{[x]}))], 
      each [y]
    ), 
    c = List.Combine(
      List.Transform(
        List.Distinct(b), 
        (x) =>
          [
            c1 = Number.RoundUp((List.PositionOf(b, x, Occurrence.Last) + 1) / (2 * x), 0), 
            c2 = {1 .. c1}, 
            c3 = 
              if Number.IsOdd(List.Count(c2)) then
                List.Reverse(List.RemoveLastN(c2, 1))
              else
                List.Reverse(c2), 
            c4 = List.Combine({c2, c3})
          ][c4]
      )
    ), 
    grup = List.Transform(List.Zip({b, c}), (x) => Text.From(x{0}) & "-" & Text.From(x{1})), 
    tbl = Table.FromColumns({tip[Date], tip[Quantity], grup}, Table.ColumnNames(tip) & {"Group"})
  ][tbl]
in
  s
Power Query solution 6 for Custom Grouping! Part 15, proposed by Vida Vaitkunaite:
let
 Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
 Group = Table.Group(Source, {"Date"}, {{"All", each 
let
a = _,
b = List.Count(a[Date]),
c = Number.RoundUp(b/2),
d = Table.AddIndexColumn(a, "Ind", 1),
e = Table.AddColumn(d, "Gr", each if [Ind]<=c then [Ind] else b+1-[Ind])
in e
}}, GroupKind.Local, (x,y)=> Number.From(Date.Month(y[Date])<>Date.Month(x[Date]))),
 Index = Table.RemoveColumns(Table.AddIndexColumn(Group, "Index", 1, 1), "Date"),
 Expand = Table.ExpandTableColumn(Index, "All", {"Date", "Quantity", "Gr"}),
 MergeCol = Table.CombineColumns(Table.TransformColumnTypes(Expand, {{"Index", type text}, {"Gr", type text}}, "en-GB"),{"Index", "Gr"},Combiner.CombineTextByDelimiter("-", QuoteStyle.None),"Group")
in
 MergeCol
// My solution will work with both even and odd number of transactions

Solving the challenge of Custom Grouping! Part 15 with Excel

Excel solution 1 for Custom Grouping! Part 15, proposed by Bo Rydobon 🇹🇭:
=LET(
    x,
    B3:B26,
    HSTACK(
        B3:C26,
        MONTH(
            x
        )&-BYROW(
            COUNTIFS(
                x,
                {">=",
                "<="}&x,
                x,
                {"<=",
                ">"}&EOMONTH(
                    +x,
                    {0,
                    -1}
                )
            ),
            MIN
        )
    )
)
Excel solution 2 for Custom Grouping! Part 15, proposed by 🇰🇷 Taeyong Shin:
=LET(d,
    B3:B26,
    p,
    DROP(PIVOTBY(B3:C26,
    MONTH(
        d
    ),
    d,
    LAMBDA(x,
    y,
    LET(n,
    XMATCH(
        x,
        y
    ),
    m,
    ROWS(
        y
    ),
    @(MONTH(
        x
    )&-IF(
        n>m/2,
        m-n+1,
        n
    )))),
    ,
    0,
    ,
    0),
    1),
    WRAPROWS(
        TOCOL(
            IFS(
                p<>"",
                p
            ),
            2
        ),
        3
    ))
Excel solution 3 for Custom Grouping! Part 15, proposed by Oscar Mendez Roca Farell:
=HSTACK(B3:C26,
    MAP(B3:B26,
    LAMBDA(b,
    LET(O,
    MONTH,
    m,
    O(
        b
    ),
    n,
    SUM(
        N(
            O(
                B3:b
            )=m
        )
    ),
    e,
    SUM(
        N(
            O(
                B3:B26
            )=m
        )
    )/2,
    m&(n>ROUND(
        e,    ))*(2*MOD(
        n-1,
        e
    )+1)-n))))
Excel solution 4 for Custom Grouping! Part 15, proposed by Julian Poeltl:
=LET(T,B2:C26,D,DROP(TAKE(T,,1),1),M,MONTH(D),HSTACK(T,VSTACK("Group",DROP(REDUCE(0,UNIQUE(M),LAMBDA(A,B,VSTACK(A,LET(C,ROWS(FILTER(M,M=B))/2,B&"-"&VSTACK(SEQUENCE(C),SEQUENCE(C,,C,-1)))))),1))))
Excel solution 5 for Custom Grouping! Part 15, proposed by Kris Jaganah:
=LET(
    a,
    B3:B26,
    b,
    XMATCH(
        a,
        a
    ),
    c,
    MONTH(
        a
    ),
    d,
    ROWS(
        UNIQUE(
            c
        )
    ),
    e,
    MAX(
        b
    ),
    f,
    MOD(
        b-1,
        e/d
    )+1,
    HSTACK(
        a,
        C3:C26,
        c&"-"&IF(
            f>e/d/2,
            e/d+1-f,
            f
        )
    )
)
Excel solution 6 for Custom Grouping! Part 15, proposed by ferhat CK:
=REDUCE(
    {"Date",
    "Quantity",
    "Group"},
    UNIQUE(
        MONTH(
            G3:G26
        )
    ),
    LAMBDA(
        x,
        y,
        VSTACK(
            x,
            LET(
                f,
                FILTER(
                    B3:C26,
                    MONTH(
                        B3:B26
                    )=y
                ),
                r,
                ROWS(
                    f
                ),
                HSTACK(
                    f,
                    VSTACK(
                        y&"-"&SEQUENCE(
                            r/2
                        ),
                        y&"-"&SEQUENCE(
                            r/2,
                            ,
                            r/2,
                            -1
                        )
                    )
                )
            )
        )
    )
)
Excel solution 7 for Custom Grouping! Part 15, proposed by Hamidi Hamid:
=LET(tt,
    COUNTA(
        UNIQUE(
            MONTH(
                B3:B26
            )
        )
    ),
    x,
    SEQUENCE(ROWS(
            MONTH(
                B3:B26
            )
        )/(tt*2)),
    y,
    SORT(
        x,
        ,
        -1
    ),
    t,
    VSTACK(
        x,
        y
    ),
    HSTACK(
        B3:C26,
        MONTH(
                B3:B26
            )&"-"&VSTACK(
                t,
                t
            )
    ))
Excel solution 8 for Custom Grouping! Part 15, proposed by Hussein SATOUR:
=HSTACK(B3:C26,
    LET(C,
    CONCAT,
    U,
    SUM,
    S,
    SEQUENCE,
    m,
    MONTH(
        B3:B26
    ),
    TEXTSPLIT(C(MAP(UNIQUE(
        m
    ),
    LAMBDA(x,
    C(x&"-"&VSTACK(S(U((m=x)*1)/2),
    S(U((m=x)*1)/2,
    ,
    U((m=x)*1)/2,
    -1))&"/")))),
    ,
    "/",
    1)))
Excel solution 9 for Custom Grouping! Part 15, proposed by Luis Enrique Charca Ponce:
=LET(data,
    B3:C26,month,
    TEXT(
        MONTH(
            TAKE(
                data,
                ,
                1
            )
        ),
        "0"
    ),summ,
    GROUPBY(
        month,
        month,
        COUNTA,
        0,
        0
    ),idRaw,
    SCAN("1-0",
    month,
    LAMBDA(ac,
    val,LET(parts,
    TEXTSPLIT(
        ac,
        "-"
    ),IF(INDEX(
    parts,
    1
)=val,
    val&"-"&
(1--INDEX(
    parts,
    2
)),
    val&"-1")))),id,
    MAP(
        idRaw,
        LAMBDA(
            i,
            
            LET(
                a,
                TEXTBEFORE(
                    i,
                    "-"
                ),
                b,
                --TEXTAFTER(
                    i,
                    "-"
                ),
                
                a&"-"&MIN(
                    b,
                    VLOOKUP(
                        a,
                        summ,
                        2,
                        FALSE
                    )+1-b
                )
            )
        )
    ),HSTACK(
    data,
    id
))
Excel solution 10 for Custom Grouping! Part 15, proposed by Md. Zohurul Islam:
=LET(
    z,
    B3:C26,
    w,
    TAKE(
        z,
        ,
        1
    ),
    p,
    MONTH(
        w
    ),
    unq,
    UNIQUE(
        p
    ),
    q,
    DROP(
        REDUCE(
            "",
            unq,
            LAMBDA(
                x,
                y,
                LET(
                    a,
                    COUNT(
                        FILTER(
                            w,
                            p=y
                        )
                    )/2,
                    b,
                    VSTACK(
                        SEQUENCE(
                            a
                        ),
                        SEQUENCE(
                            a,
                            ,
                            a,
                            -1
                        )
                    ),
                    d,
                    y&"-"&b,
                    e,
                    VSTACK(
                        x,
                        d
                    ),
                    e
                )
            )
        ),
        1
    ),
    s,
    VSTACK(
        HSTACK(
            B2:C2,
            "Group"
        ),
        HSTACK(
            z,
            q
        )
    ),
    s
)
Excel solution 11 for Custom Grouping! Part 15, proposed by Pieter de B.:
=LET(
    m,
    MONTH(
        G3:G26
    ),
    u,
    UNIQUE(
        m
    ),
    REDUCE(
        "Group",
        u,
        LAMBDA(
            x,
            y,
            LET(
                r,
                SEQUENCE(
                    ROWS(
                        FILTER(
                            m,
                            m=y
                        )
                    )
                ),
                VSTACK(
                    x,
                    MATCH(
                        y,
                        u
                    )&-IF(
                        r>MAX(
                            r
                        )/2,
                        SORT(
                            r,
                            ,
                            -1
                        ),
                        r
                    )
                )
            )
        )
    )
)

Solving the challenge of Custom Grouping! Part 15 with Python

Python solution 1 for Custom Grouping! Part 15, proposed by Konrad Gryczan, PhD:
import pandas as pd

path = "CH-173 Custom Grouping.xlsx"
input = pd.read_excel(path, usecols="B:C", skiprows=1, nrows=25)
test = pd.read_excel(path, usecols="G:I", skiprows=1, nrows=25).rename(columns=lambda x: x.split('.')[0])

input['Month'] = input['Date'].dt.month
input['Group'] = input.groupby('Month').cumcount() + 1
input['Group'] = input.apply(lambda x: f"{x['Month']}-{min(x['Group'], len(input[input['Month'] == x['Month']]) - x['Group'] + 1)}", axis=1)

result = input[['Date','Quantity', 'Group']]
print(result.equals(test)) # Test

Solving the challenge of Custom Grouping! Part 15 with Python in Excel

Python in Excel solution 1 for Custom Grouping! Part 15, proposed by Alejandro Campos:
import defaultdict
from datetime import datetime

df = xl("B2:C26", headers=True)
df['Date'] = pd.to_datetime(df['Date'], format="%d/%m/%Y").dt.strftime('%Y-%m-%d')

transactions_by_month = defaultdict(list)
for i, row in df.iterrows():
 transactions_by_month[datetime.strptime(row["Date"], "%Y-%m-%d").strftime("%m/%Y")].append((row["Date"], row["Quantity"]))

grouped_transactions = []
for idx, (month, transactions) in enumerate(transactions_by_month.items(), 1):
 n = len(transactions)
 for i in range((n + 1) // 2):
 group_number = f"{idx}-{i+1}"
 grouped_transactions.append((transactions[i][0], transactions[i][1], group_number))
 if i != n - i - 1:
 grouped_transactions.append((transactions[n - i - 1][0], transactions[n - i - 1][1], group_number))

grouped_transactions_df = pd.DataFrame(grouped_transactions, columns=["Date", "Quantity", "Group"])
grouped_transactions_df['Quantity'] = grouped_transactions_df['Quantity'].astype(float)
df['Quantity'] = df['Quantity'].astype(float)
result_df = pd.merge(df, grouped_transactions_df, on=["Date", "Quantity"], how="left")
result_df['Date'] = pd.to_datetime(result_df['Date']).dt.strftime('%d/%m/%Y')
result_df

Solving the challenge of Custom Grouping! Part 15 with R

R solution 1 for Custom Grouping! Part 15, proposed by Konrad Gryczan, PhD:
library(tidyverse)
library(readxl)

path = "files/CH-173 Custom Grouping.xlsx"
input = read_excel(path, range = "B2:C26")
test = read_excel(path, range = "G2:I26")

result = input %>%
 group_by(month(Date)) %>%
 mutate(Group = paste0(month(Date), "-", pmin(row_number(), rev(row_number())))) %>%
 ungroup() %>%
 select(Date, Quantity, Group)

all.equal(result, test, check.attributes = FALSE)
#> [1] TRUE

Solving the challenge of Custom Grouping! Part 15 with Google Sheets

Google Sheets solution 1 for Custom Grouping! Part 15, proposed by QABBAL HICHAM:
Can you propose this challenge: https://docs.google.com/spreadsheets/d/1toc6rQTOHOe3uDWtEdYxukZdwdImvM2As-7wq1DUjDU/edit?usp=drivesdk
Google Sheets solution 2 for Custom Grouping! Part 15, proposed by Peter Krkos:
PowerQuery solution:
https://docs.google.com/spreadsheets/d/1zR5IZLz8OT76vhaPEHfsPrw8-RDKnLyyqS49IJjdhFk/edit?pli=1&gid=112402506#gid=112402506

Leave a Reply