Home » Custom Grouping! Part 4

Custom Grouping! Part 4

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

The stock prices for the given dates are provided in the table. Identify how many times the price follows an upward trend and how many times it follows a downward trend. Upward trends are shown in green, while downward trends are shown in red. A trend is defined as a movement in the same direction for more than two consecutive dates.

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

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

Power Query solution 1 for Custom Grouping! Part 4, proposed by Zoran Milokanović:
let
  Source = Excel.CurrentWorkbook(){[Name = "Input"]}[Content][Stock price], 
  S = Table.Group(
    Table.FromList(
      List.Accumulate(
        Source, 
        {}, 
        (b, n) =>
          let
            l = List.Last(b), 
            d = if n > l{0} then "Upward" else "Downward"
          in
            {b & {{n, null}}, List.RemoveLastN(b) & {{n, d}}}{
              Number.From(b <> {} and (l{1} = null or l{1} = d))
            }
      ), 
      each {_{1}}, 
      {"Group"}
    ), 
    "Group", 
    {"Times", Table.RowCount}
  )
in
  S
Power Query solution 2 for Custom Grouping! Part 4, proposed by Brian Julius:
let
  Source = Excel.CurrentWorkbook(){[Name = "Table1"]}[Content], 
  AddIdx = Table.AddIndexColumn(Source, "Index"), 
  AddPrev = Table.AddColumn(
    AddIdx, 
    "PrevPrice", 
    each try Source[Stock price]{[Index] - 1} otherwise null
  ), 
  AddGrp = Table.AddColumn(
    AddPrev, 
    "Grp", 
    each try
      
        if [Stock price] > [PrevPrice] then
          "Upward"
        else if [Stock price] < [PrevPrice] then
          "Downward"
        else
          "Same"
    otherwise
      null
  ), 
  FillUp = Table.FillUp(AddGrp, {"Grp"}), 
  Add1stOrd = Table.AddColumn(
    FillUp, 
    "FirstOrd", 
    each try if [Grp] = FillUp[Grp]{[Index] - 1} then null else [Index] otherwise [Index]
  ), 
  Add2ndOrd = Table.AddColumn(
    Add1stOrd, 
    "SecondOrd", 
    each try
      if Add1stOrd[FirstOrd]{[Index] - 1} = null then [FirstOrd] else null
    otherwise
      [FirstOrd]
  ), 
  AddGroup = Table.AddColumn(Add2ndOrd, "Group", each if [SecondOrd] = null then [Grp] else null), 
  FillDown = Table.FillDown(AddGroup, {"SecondOrd"}), 
  FillUp2 = Table.FillUp(FillDown, {"Group"}), 
  RemOther = Table.Distinct(Table.SelectColumns(FillUp2, {"SecondOrd", "Group"})), 
  GpCount = Table.Group(RemOther, {"Group"}, {{"Times", each Table.RowCount(_)}})
in
  GpCount
Power Query solution 3 for Custom Grouping! Part 4, proposed by Alejandro Simón 🇵🇦 🇪🇸:
let
  Source = Excel.CurrentWorkbook(){[Name = "Table1"]}[Content], 
  UpDown = Table.FromColumns(
    {
      List.Transform(
        {0 .. Table.RowCount(Source) - 1}, 
        each 
          if ({0} & Source[Stock price]){_ + 1} > ({0} & Source[Stock price]){_} then
            "Upward"
          else
            "Downward"
      )
    }, 
    {"A"}
  ), 
  Sol = Table.FromRows(
    List.Transform(
      {"Upward", "Downward"}, 
      each {
        _, 
        List.Count(
          List.Select(
            Table.SelectRows(
              Table.Group(UpDown, {"A"}, {{"B", each List.Count([A])}}, 0), 
              each [B] > 1
            )[A], 
            (x) => x = _
          )
        )
      }
    ), 
    {"Group", "Times"}
  )
in
  Sol
Power Query solution 4 for Custom Grouping! Part 4, proposed by Kris Jaganah:
let
  A = Excel.CurrentWorkbook(){[Name = "Table1"]}[Content], 
  B = Table.AddColumn(
    A, 
    "Diff", 
    each [Stock price] - (try A[Stock price]{List.PositionOf(A[Date], [Date]) - 1} otherwise 0)
  ), 
  C = Table.AddIndexColumn(B, "Id", 1), 
  D = Table.AddColumn(
    C, 
    "Group", 
    each List.Accumulate(
      List.Range(B[Diff], 0, [Id]), 
      0, 
      (x, y) =>
        if y > 0 and x > 0 then
          x + 1
        else if y > 0 and x <= 0 then
          1
        else if y < 0 and x > 0 then
          - 1
        else
          x - 1
    )
  ), 
  E = Table.Group(D, {"Group"}, {"Times", each Table.RowCount(_)}), 
  F = Table.SelectRows(E, each Number.Abs([Group]) = 2), 
  G = Table.TransformColumns(F, {"Group", each if _ > 0 then "Upwards" else "Downwards"})
in
  G
Power Query solution 5 for Custom Grouping! Part 4, proposed by 🇮🇷 Navid Esmaeilzadeh اسماعیل زاده:
let
  S = Excel.CurrentWorkbook(){[Name = "Table1"]}[Content], 
  A = Table.AddIndexColumn(S, "i", 0, 1), 
  B = Table.AddColumn(
    A, 
    "d", 
    each 
      if (try [Stock price] - A[Stock price]{[i] - 1} otherwise [Stock price]) >= 0 then
        "Up"
      else
        "Down"
  ), 
  C = Table.AddColumn(B, "d2", each if [d] = B[d]{[i] - 1} then "T" else "N"), 
  D = Table.AddColumn(
    C, 
    "d3", 
    each try if [d2] = "N" and C[d2]{[i] - 1} <> "N" then [i] else null otherwise [i]
  ), 
  E = Table.FillDown(D, {"d3"}), 
  F = Table.Group(E, {"d", "d3"}, {{"Count", each Table.RowCount(_), Int64.Type}}), 
  G = Table.SelectRows(F, each [Count] <> 1), 
  H = Table.Group(G, {"d"}, {{"Count", each Table.RowCount(_), Int64.Type}}), 
  #"Renamed Columns" = Table.RenameColumns(H, {{"d", "Group"}, {"Count", "Time"}})
in
  #"Renamed Columns"

Solving the challenge of Custom Grouping! Part 4 with Excel

Excel solution 1 for Custom Grouping! Part 4, proposed by Bo Rydobon 🇹🇭:
=LET(z,N(C4:C26>C3:C25),L,LAMBDA(i,IF({1,0},IF(i,"Up","Down")&"ward",SUM(N(SCAN(1,z=i,LAMBDA(a,v,a*v+v))=3)))),VSTACK(L(1),L(0)))
Excel solution 2 for Custom Grouping! Part 4, proposed by Oscar Mendez Roca Farell:
=LET(
    d,
     N(
         C3:C26>C2:C25
     ),
     r,
     ROW(
         B3:B26
     ),
     F,
     LAMBDA(
         i,
          SUM(
              N(
                  FREQUENCY(
                      IF(
                          i,
                           r
                      ),
                       IF(
                           1-i,
                            r
                       )
                  )>1
              )
          )
     ),
     HSTACK(
         VSTACK(
             "Up",
              "Down"
         )&"ward",
          VSTACK(
              F(
                  d
              ),
               F(
                   1-d
               )
          )
     )
)
Excel solution 3 for Custom Grouping! Part 4, proposed by Julian Poeltl:
=LET(A,
    C3:C26,
    D,
    DROP(
        A>DROP(
            A,
            1
        ),
        -1
    ),
    C,
    DROP((D=TRUE)*(DROP(
        D,
        1
    )=FALSE),
    -1),
    S,
    SUM(
        C
    ),
    HSTACK(
        VSTACK(
            "Upward",
            "Downward"
        ),
        VSTACK(
            IF(
                INDEX(
            A,
            1
        )
Excel solution 4 for Custom Grouping! Part 4, proposed by Kris Jaganah:
=LET(a,
    C3:C26,
    b,
    a-VSTACK(
        0,
        DROP(
            a,
            -1
        )
    ),
    c,
    SCAN(0,
    b,
    LAMBDA(x,
    y,
    IFS((y>0)*(x>0),
    1+x,
    (y>0)*(x<=0),
    1,
    (y<0)*(x>0),
    -1,
    1,
    x-1))),
    VSTACK(
        SUM(
            N(
                c=2
            )
        ),
        SUM(
            N(
                c=-2
            )
        )
    ))
Excel solution 5 for Custom Grouping! Part 4, proposed by JvdV -:
=LET(
    x,
    IF(
        TOCOL(
            -REGEXEXTRACT(
                CONCAT(
                    0,
                    N(
                        C4:C26
Excel solution 6 for Custom Grouping! Part 4, proposed by Imam Hambali:
=LET(
a,
     C3:C26,c,
     IF(
         a-VSTACK(
             0,
              DROP(
                  a,
                  -1
              )
         )>0,
         1,
         -1
     ),f,
     SCAN(
         0,
          IF(
              c=VSTACK(
                  0,
                   DROP(
                       c,
                       -1
                   )
              ),
              c,
              0
          ),
          LAMBDA(
              x,
              y,
               IF(
                   y=0,
                   0,
                   x+y
               )
          )
     ),g,
     IF(FILTER(f,
    (f=-2)+(f=2))>0,
    "Upward",
    "Downward"),GROUPBY(
    g,
    g,
    COUNTA,
    0,
    0
)
)
Excel solution 7 for Custom Grouping! Part 4, proposed by Sunny Baggu:
=LET(     _i,
     {101; 100; 11},     _d,
     {110; 10},     _t,
     CONCAT(
         N(
             C4:C26 > C3:C25
         )
     ),     l,
     LAMBDA(
         arr,          ROWS(
              
               MAP(
                   
                    arr,
                   
                    LAMBDA(
                        a,
                        
                         ROWS(
                             UNIQUE(
                                 TOCOL(
                                     SEARCH(
                                         a,
                                          _t,
                                          SEQUENCE(
                                              LEN(
                                                  _t
                                              )
                                          )
                                     ),
                                      3
                                 )
                             )
                         )
                         
                    )
                    
               )
               
          )     ),     HSTACK(
         {"Upward "; "Downward"},
          VSTACK(
              l(
                  _i
              ),
               l(
                   _d
               )
          )
     ))
Excel solution 8 for Custom Grouping! Part 4, proposed by Bilal Mahmoud kh.:
=LET(
    a,
    MAP(
        C3:C26,
        LAMBDA(
            x,
            IF(
                x-INDEX(
                    C3:x,
                    COUNT(
                        C3:x
                    )-1,
                    1
                )>=0,
                1,
                0
            )
        )
    ),
    b,
    TEXTSPLIT(
        CONCAT(
            a
        ),
        "0",
        ,
        TRUE
    ),
    c,
    TEXTSPLIT(
        CONCAT(
            a
        ),
        "1",
        ,
        TRUE
    ),
    HSTACK(
        {"Upward";"Downward"},
        VSTACK(
            COUNTA(
                b
            )-1,
            COUNTA(
                c
            )-1
        )
    )
)
Excel solution 9 for Custom Grouping! Part 4, proposed by David-Olivier Tarac:
= LAMBDA(data; LET(
 seq;
    SEQUENCE(
        ROWS(
            data
        )
    ); updown;
    IFERROR(
        data/VSTACK(
            {0};
            TAKE(
                data;
                ROWS(
            data
        )-1
            )
        );
        ""
    ); ups;
    IF(
        updown>1;
        seq
    ); downs;
    IF(
        updown<1;
        seq
    ); frequps;
    FREQUENCY(
        ups;
        downs
    ); freqdowns;
    FREQUENCY(
        downs;
        ups
    ); nbclustersup;
    SUM(1*(frequps>1)); nbclustersdown;
    SUM(1*(freqdowns>1)); VSTACK(
     nbclustersup;
     nbclustersdown
 )
 )
)
Excel solution 10 for Custom Grouping! Part 4, proposed by Eddy Wijaya:
=LET(    d,
    C3:C26,    up,
    SCAN(
        0,
        d,
        LAMBDA(
            a,
            v,
            IF(
                v>OFFSET(
                    v,
                    -1,
                    
                ),
                a+1,
                a*0
            )
        )
    ),    dw,
    SCAN(
        0,
        d,
        LAMBDA(
            a,
            v,
            IF(
                v
Excel solution 11 for Custom Grouping! Part 4, proposed by Hussein SATOUR:
=LET(s,
    C2:C26,
    a,
    DROP(
        s,
        -2
    ),
    b,
    DROP(
        DROP(
            s,
            1
        ),
        -1
    ),
    e,
    VSTACK({1;0},
    ((DROP(
        s,
        2
    )-b)*IFERROR(
        b-a,
        0
    )>=0)*1),
    x,
    TOCOL(IFS(e<>0,
    1/0,
    VSTACK(
        0,
        DROP(
            e,
            -1
        )
    )=0,
    1/0,
    (e=0)*((VSTACK(
        b,
        TAKE(
            s,
            -1
        ),
        0
    )-IFNA(
        a,
        s
    ))>0),
    "Upward",
    1,
    "Downward"),
    2),
    GROUPBY(
        x,
        x,
        COUNTA
    ))
Excel solution 12 for Custom Grouping! Part 4, proposed by Talia Cao, CPA:
=LET(p,
    C3:C26,
    c,
    SIGN(
        p-N(
            +OFFSET(
                p,
                -1,
                
            )
        )
    ),
    MAP(IF(
        G3:G4=G3,
        1,
        -1
    ),
    LAMBDA(m,
    SUM(--(SCAN(
        0,
        c,
        LAMBDA(
            a,
            v,
            IF(
                v=m,
                a+1
            )
        )
    )=3)))))

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

Python in Excel solution 1 for Custom Grouping! Part 4, proposed by Alejandro Campos:
df = xl("B2:C26", headers=True)
df['Date'] = pd.to_datetime(df['Date'], format='%d/%m/%Y')
df['Price Change'] = df['Stock price'].diff()
df['Trend'] = df['Price Change'].apply(lambda x: 'Up' if x > 0 else ('Down' if x < 0 else 'No Change'))
counts = df['Trend'].groupby((df['Trend'] != df['Trend'].shift()).cumsum()).agg(['first', 'size'])
result = counts[counts['size'] >= 2].groupby('first').size().reindex(['Up', 'Down'], fill_value=0).reset_index()
result.columns = ['Group', 'Times']
result
Python in Excel solution 2 for Custom Grouping! Part 4, proposed by Ümit Barış Köse, MSc:
df = xl("B2:C26", headers=True)
def count_trends(prices):
 upward_count = 0
 downward_count = 0
 upward_streak = 0
 downward_streak = 0 
 for i in range(1, len(prices)):
 if prices[i] > prices[i - 1]:
 upward_streak += 1
 downward_streak = 0
 elif prices[i] < prices[i - 1]:
 downward_streak += 1
 upward_streak = 0
 upward_count += upward_streak == 2
 downward_count += downward_streak == 2 
 return upward_count, downward_count
upward_count, downward_count = count_trends(df['Stock price'].values)
result = pd.DataFrame({
 'Group': ['Upward', 'Downward'],
 'Times': [upward_count, downward_count]
})
result
Python in Excel solution 3 for Custom Grouping! Part 4, proposed by George Mount:
xlsx
# Data import 
df = xl("B2:C26", headers=True)
# Calculate the daily price difference and its sign
df['Diff'] = df['Stock price'].diff()
df['Sign'] = df['Diff'].apply(lambda x: 1 if x > 0 else (-1 if x < 0 else 0))
# Drop the first row with NaN values
df = df.dropna(subset=['Sign'])
# Identify sequences where the sign changes
df['Group'] = (df['Sign'] != df['Sign'].shift()).cumsum()
# Group by sequences and filter trends lasting more than two dates
grouped = df.groupby('Group')['Sign'].agg(['size', 'first']).reset_index()
trends = grouped[grouped['size'] >= 2]
# Count upward and downward trends
upward_trends = trends[trends['first'] == 1]['Group'].count()
downward_trends = trends[trends['first'] == -1]['Group'].count()
# Prepare the result
result = pd.DataFrame({
 'Group': ['Upward', 'Downward'],
 'Times': [upward_trends, downward_trends]
})
result

Solving the challenge of Custom Grouping! Part 4 with R

R solution 1 for Custom Grouping! Part 4, proposed by Konrad Gryczan, PhD:
library(tidyverse)
library(readxl)
path = "files/CH-119 Custom Grouping.xlsx"
input = read_excel(path, range = "B2:C26") %>% janitor::clean_names()
test = read_excel(path, range = "G2:H4")
result = input %>%
 mutate(a = cumsum(lag(stock_price, default = 0) > stock_price),
 d = cumsum(lag(stock_price, default = 0) < stock_price)) %>%
 mutate(a_n = ifelse(n() > 2, a, NA), .by = a) %>%
 mutate(d_n = ifelse(n() > 2, d, NA), .by = d) %>%
 mutate(check = case_when(
 !is.na(a_n) & is.na(d_n) ~ a_n,
 !is.na(d_n) & is.na(a_n) ~ d_n,
 !is.na(a_n) & !is.na(d_n) ~ pmin(a_n, d_n),
 TRUE ~ NA_real_
 )) %>%
 mutate(diff = stock_price - lag(stock_price, default = 0)) %>%
 mutate(sign = sign(median(diff)), 
 Group = ifelse(sign == 1, "Upward", "Downward"),
 .by = check) %>%
 summarise(Times = n_distinct(check), .by = Group)
all.equal(result, test)
#> [1] TRUE

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

Google Sheets solution 1 for Custom Grouping! Part 4, proposed by Peter Krkos:
PowerQuery solution:
https://docs.google.com/spreadsheets/d/1zR5IZLz8OT76vhaPEHfsPrw8-RDKnLyyqS49IJjdhFk/edit?usp=sharing

Leave a Reply