pythonpandasreportlab

Order columns in dataframe before converting to report lab


I am creating a pdf report using report lab based on multiple pandas dataframes.

The tables that are displayed in the pdf need to have to columns in a specific order.

here is a sample dataframe:

df = pd.DataFrame({
        'Counterparty': ['foo', 'fizz', 'fizz', 'fizz','fizz', 'foo'],
        'Commodity': ['bar', 'bar', 'bar', 'bar','bar', 'ab cred'],
        'DealType': ['Buy', 'Buy', 'Buy', 'Buy', 'Buy', 'Buy'],
        'StartDate': ['07/01/2024', '09/01/2024', '10/01/2024', '11/01/2024', '12/01/2024', '01/01/2025'],
        'FloatPrice': [18.73, 17.12, 17.76, 18.72, 19.47, 20.26],
        'MTMValue':[10, 10, 10, 10, 10, 10]
        })

out = pd.pivot_table(df, values = 'MTMValue', index='Counterparty', columns = 'Commodity', aggfunc='sum').reset_index().rename_axis(None, axis=1).fillna(0)
out['Cumulative Exposure'] = out[out.columns[1:]].sum(axis = 1)

 Counterparty  ab cred   bar  Cumulative Exposure
0         fizz      0.0  40.0                 40.0
1          foo     10.0  10.0                 20.0

I need this:

Counterparty      bar   ab cred  Cumulative Exposure
0         fizz     40.0  0.0                 40.0
1          foo     10.0  10.0                 20.0

where ab cred will always be before cumulative exposure and after the last commodity, since there can be x amount of commodities added to the data (ab cred is always included as a commodity). More columns can also be added after cumulative exposure at any time.


Solution

  • One easy option would be to pop and reinsert ab cred so that it ends up last:

    out = (pd.pivot_table(df, values='MTMValue', index='Counterparty',
                          columns='Commodity', aggfunc='sum')
             .assign(**{'ab cred': lambda x: x.pop('ab cred')})
             .reset_index().rename_axis(None, axis=1).fillna(0)
          )
    

    Another option would be to sort_index with a custom key:

    out = (pd.pivot_table(df, values='MTMValue', index='Counterparty',
                          columns='Commodity', aggfunc='sum')
             .sort_index(axis=1, key=lambda x: x == 'ab cred', kind='stable')
             .reset_index().rename_axis(None, axis=1).fillna(0)
          )
    

    And, of course, if you know the explicit order that is desired you can always reindex (and also note that you can avoid the fillna and later assignment of the Cumulative Exposure column):

    order = ['bar', 'ab cred']
    
    out = (pd.pivot_table(df, values='MTMValue', index='Counterparty',
                          columns='Commodity', aggfunc='sum', fill_value=0)
             .reindex(columns=order)
             .assign(**{'Cumulative Exposure': lambda x: x.sum(axis=1)})
             .reset_index().rename_axis(None, axis=1)
          )
    

    Output:

      Counterparty   bar  ab cred  Cumulative Exposure
    0         fizz  40.0      0.0                 40.0
    1          foo  10.0     10.0                 20.0