pandasdatetimemappinggroupingpandas-explode

Distributing Values in a Pandas dataframe with different time intervals


I have a pandas data frame which has the data sorted into quarters like this: ( I am showing a sample data)

 df 

  id    qtr value
  a     Q01 100
  a     Q02 130
  a     Q03 160
  a     Q04 100
  b     Q01 1000
  b     Q02 1300
  b     Q03 1600
  b     Q04 1000

Now the problem what I want to solve is to redistribute the quarter values to months as follows: I want to distribute the quarter value into 3 and assign first two months that value and the remaining to the last month in the quarter. So my output should be like follows

    outdf 


        id  qtr   mnth  value
         a  Q01  M01     30
         a  Q01  M02     30
         a  Q01  M03     40
         a  Q02  M04     40
         a  Q02  M05     40
         a  Q02  M06     50
         a  Q03  M07     50
         a  Q03  M08    50
         a  Q03  M09    60
         a  Q04  M10    30
         a  Q04  M11    30
         a  Q04  M12    40
         b  Q01  M01    300
         b  Q01  M02    300
         b  Q01  M03    400
         b  Q02  M04    400
         b  Q02  M05    400
         b  Q02  M06    500
         b  Q03  M07    500
         b  Q03  M08    500
         b  Q03  M09    600
         b  Q04  M10    300
         b  Q04  M11    300
         b  Q04  M12    400

So what I till now tried is as follows:

I created a mapping for quarters:

   quarters = {'Q01': ['M01','M02','M03'], 'Q02': ['M04','M05','M06'],
        'Q03': ['M07','M08','M09'], 'Q04': ['M10', 'M11', 'M12']}

and the tried to melt and explode the dataframe using this trick:

  out = (df.melt(['id'], value_name='value', var_name='qtr')
          .assign(rev=lambda d: d['value'],#.div(3),
          qtr=lambda d: d['qtr'].str[-2:].map(quarters)
         ).explode('qtr'))

Unfortunately its not doing what I want. Any help as to how to achieve my output will be helpful and appreciated.


Solution

  • Just tried solving them in a little lengthy way. Hope this approach helps!

    For now, I am using some custom roundings but, i hope you can continue from this

    Below is the code

    import pandas as pd
    
    data = {'id': ['a', 'a', 'a', 'a', 'b', 'b', 'b', 'b'],
            'qtr': ['Q01', 'Q02', 'Q03', 'Q04', 'Q01', 'Q02', 'Q03', 'Q04'],
            'value': [100, 130, 160, 100, 1000, 1300, 1600, 1000]}
    
    df = pd.DataFrame(data)
    
    quarters_to_months = {
        'Q01': ['M01', 'M02', 'M03'],
        'Q02': ['M04', 'M05', 'M06'],
        'Q03': ['M07', 'M08', 'M09'],
        'Q04': ['M10', 'M11', 'M12']
    }
    
    result_df = pd.DataFrame(columns=['id', 'qtr', 'mnth', 'value'])
    
    for _, row in df.iterrows():
        qtr = row['qtr']
        months = quarters_to_months[qtr]
        for i, month in enumerate(months):
            if i == 2:
                result_df = pd.concat([result_df,pd.DataFrame({'id': row['id'], 'qtr': qtr, 'mnth': month, 'value': (row['value'] * 0.4)}, index=[0])], ignore_index=True)
            else:
                result_df = pd.concat([result_df,pd.DataFrame({'id': row['id'], 'qtr': qtr, 'mnth': month, 'value': (row['value'] * 0.3)}, index=[0])], ignore_index=True)
    
    result_df.sort_values(by=['id', 'qtr', 'mnth'], inplace=True)
    
    result_df.reset_index(drop=True, inplace=True)
    
    
    
    def custom_round(value):
        if(value>100):
            return round(value, -2)
        else:
            return round(value, -1)
    
    result_df['value'] = result_df['value'].apply(custom_round)
    
    print(result_df)
    

    Output

    enter image description here