Search code examples
dataframeplotlybar-chartpolar-coordinatesplotly-express

Barplot based on coloured sectors


I am creating a bar plot where the colour of the bar is proportional to the value. However, is it possible to break down the bar into colour sectors? See below what I have and what I mean:

import pandas as pd
import plotly.express as px
df = px.data.wind()
df_test = df[df["strength"]=='0-1']

fig=px.bar_polar(df_test,r='frequency',theta='direction', color='frequency',color_continuous_scale='YlGn')

fig.show()

What code creates

Below is what I want to achieve. Is it possible to have the polar sections instead of the actuar bars? Maybe at least add the horizontal axis to the bars? Or is there any other plotly chart type for this? Target plot


Solution

  • You can create linearly spaced arrays for each direction in df_test. Currently df_test looks like the following:

       direction strength  frequency
    0          N      0-1        0.5
    1        NNE      0-1        0.6
    2         NE      0-1        0.5
    3        ENE      0-1        0.4
    4          E      0-1        0.4
    5        ESE      0-1        0.3
    6         SE      0-1        0.4
    7        SSE      0-1        0.4
    8          S      0-1        0.6
    9        SSW      0-1        0.4
    10        SW      0-1        0.5
    11       WSW      0-1        0.6
    12         W      0-1        0.6
    13       WNW      0-1        0.5
    14        NW      0-1        0.4
    15       NNW      0-1        0.1
    

    What we want is for each direction to have linearly spaced frequencies between 0.1 and the ending frequency value in df_test for that particular direction, such as the following:

       direction strength  frequency
     0         N      0-1        0.1
     1         N      0-1        0.2
     2         N      0-1        0.3
     3         N      0-1        0.4
     4         N      0-1        0.5
     5       NNE      0-1        0.1
     ...
    

    We can perform a groupby on direction for df_test, then use np.arange to obtain the linearly spaced array for that particular direction, and build the resulting dataframe using pd.concat. Then we can sort the new DataFrame so the directions are in the correct order (as this is the format that px.bar_polar expects).

    import numpy as np
    import pandas as pd
    import plotly.express as px
    
    df = px.data.wind()
    df_test = df[df["strength"]=='0-1']
    
    df_test_sectors = pd.DataFrame(columns=df_test.columns)
    
    ## this only works if each group has one row
    for direction, df_direction in df_test.groupby('direction'):
        frequency_stop = df_direction['frequency'].tolist()[0]
        frequencies = np.arange(0.1, frequency_stop+0.1, 0.1)
        df_sector = pd.DataFrame({
            'direction': [direction]*len(frequencies),
            'strength': ['0-1']*len(frequencies),
            'frequency': frequencies
        })
        df_test_sectors = pd.concat([df_test_sectors, df_sector])
    df_test_sectors = df_test_sectors.reset_index(drop=True)
    df_test_sectors['direction'] = pd.Categorical(
        df_test_sectors['direction'], 
        df_test.direction.tolist() #sort the directions into the same order as those in df_test
    )
    df_test_sectors['frequency'] = df_test_sectors['frequency'].astype(float)
    df_test_sectors = df_test_sectors.sort_values(['direction', 'frequency'])
    
    fig = px.bar_polar(df_test_sectors, r='frequency', theta='direction', color='frequency', color_continuous_scale='YlGn')
    
    fig.show()
    

    enter image description here