pythonplotlyplotly-pythonsubplotplotly.graph-objects

How to change the size and spacing of Plotly's sublopts?


How is it possible to manipulate the spacing between rows(e.g. increasing the space between fist two rows and final two rows) and the size of charts inside the figure (e.g. making pie chart bigger)?

In my example, I'm trying to visualize multiple data columns from Telco Customer Churn dataset (download 176kB) using plotly's FigureWidget and make_subplots. enter image description here

This code loops through 8 columns, adds 1 pie chart and one bar chart for each column.

import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots

# Read data
df = pd.read_csv("./WA_Fn-UseC_-Telco-Customer-Churn.csv")
df['SeniorCitizen'] = df['SeniorCitizen'].map({0: 'No', 1: 'Yes'})

# Define subplot titles and data columns
data_cols = ['PhoneService' ,'MultipleLines' ,'InternetService' ,'OnlineBackup' ,'DeviceProtection' ,'TechSupport' ,'StreamingTV' ,'StreamingMovies']
titles = ['Phone Service' ,'Multiple Lines' ,'Internet Service' ,'Online Backup' ,'Device Protection' ,'Tech Support' ,'Streaming TV' ,'Streaming Movies']

fig = go.FigureWidget(make_subplots(rows=4, cols=4, specs=[
                                                        [{'type':'domain'}, {'type':'domain'}, {'type':'domain'}, {'type':'domain'}],
                                                        [{'type':'xy'}, {'type':'xy'}, {'type':'xy'}, {'type':'xy'}],
                                                        [{'type':'domain'}, {'type':'domain'}, {'type':'domain'}, {'type':'domain'}],
                                                        [{'type':'xy'}, {'type':'xy'}, {'type':'xy'}, {'type':'xy'}]]))

row, col = 1, 0
for i, (title, data_col) in enumerate(zip(titles, data_cols)):
    row, col = divmod(i, 4)
    row = row * 2
    
    # Get value counts for pie chart
    value_counts = df[data_col].value_counts()
    # Create pie chart trace and add to subplot
    pie_chart = go.Pie(labels=value_counts.index, values=value_counts.to_numpy(), name=title, title=title)
    fig.add_trace(pie_chart, row=row+1, col=col+1)
 
    # get churn rates
    churn_counts = df.groupby([data_col, 'Churn'])['Churn'].count().unstack()
    # Create stacked bar charts
    t1 = go.Bar(name='Churn (yes)', x=churn_counts['Yes'].index, y=churn_counts['Yes'])
    t2 = go.Bar(name='Churn (no)', x=churn_counts['No'].index, y=churn_counts['No'], marker_color='indianred')
    fig.add_trace(t1, row=row+2, col=col+1)
    fig.add_trace(t2, row=row+2, col=col+1)


fig.update_layout(title="Distribution of Customer Services", barmode='stack', showlegend=False)
fig.show()

Edit: this issue is not fixed with decreasing the number of columns to two either. this is the chart on a large wide screen: enter image description here

import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots

# Read data
df = pd.read_csv("./WA_Fn-UseC_-Telco-Customer-Churn.csv")
df['SeniorCitizen'] = df['SeniorCitizen'].map({0: 'No', 1: 'Yes'})

# Define subplot titles and data columns
data_cols = ['PhoneService' ,'MultipleLines' ,'InternetService' ,'OnlineBackup' ,'DeviceProtection' ,'TechSupport' ,'StreamingTV' ,'StreamingMovies']
titles = ['Phone Service' ,'Multiple Lines' ,'Internet Service' ,'Online Backup' ,'Device Protection' ,'Tech Support' ,'Streaming TV' ,'Streaming Movies']

fig = go.FigureWidget(make_subplots(rows=8, cols=2, specs=[
                                                        [{'type':'domain'}, {'type':'domain'}],
                                                        [{'type':'xy'}, {'type':'xy'}],
                                                        [{'type':'domain'}, {'type':'domain'}],
                                                        [{'type':'xy'}, {'type':'xy'}],
                                                        [{'type':'domain'}, {'type':'domain'}],
                                                        [{'type':'xy'}, {'type':'xy'}],
                                                        [{'type':'domain'}, {'type':'domain'}],
                                                        [{'type':'xy'}, {'type':'xy'}]]))

row, col = 1, 0
for i, (title, data_col) in enumerate(zip(titles, data_cols)):
    row, col = divmod(i, 2)
    row = row * 2

    # Get value counts for pie chart
    value_counts = df[data_col].value_counts()
    # Create pie chart trace and add to subplot
    pie_chart = go.Pie(labels=value_counts.index, values=value_counts.to_numpy(), name=title, title=title)
    fig.add_trace(pie_chart, row=row+1, col=col+1)

    # get churn rates
    churn_counts = df.groupby([data_col, 'Churn'])['Churn'].count().unstack()
    # Create stacked bar charts
    t1 = go.Bar(name='Churn (yes)', x=churn_counts['Yes'].index, y=churn_counts['Yes'])
    t2 = go.Bar(name='Churn (no)', x=churn_counts['No'].index, y=churn_counts['No'], marker_color='indianred')
    fig.add_trace(t1, row=row+2, col=col+1)
    fig.add_trace(t2, row=row+2, col=col+1)


fig.update_layout(title="Distribution of Customer Services", barmode='stack', showlegend=False)
fig.show()


Solution

  • The spacing between rows and columns can be adjusted with vertical_spacing and horizontal_spacing arguments respectively. Both can have a float value between 0 and 1/(size -1). It takes some tinkering to find the right value that suits you. To maximize the size of the charts within the figure you can update the margin argument within the fig.update_layout call. All four sides are referred to in a dictionary with the keys: t, b, l and r.

    The code below is an alteration of yours that using all these arguments.

    import pandas as pd
    import plotly.graph_objects as go
    from plotly.subplots import make_subplots
    
    # Read data
    df = pd.read_csv(".../WA_Fn-UseC_-Telco-Customer-Churn.csv")
    df['SeniorCitizen'] = df['SeniorCitizen'].map({0: 'No', 1: 'Yes'})
    
    # Define subplot titles and data columns
    data_cols = ['PhoneService' ,'MultipleLines' ,'InternetService' ,'OnlineBackup' ,'DeviceProtection' ,'TechSupport' ,'StreamingTV' ,'StreamingMovies']
    titles = ['Phone Service' ,'Multiple Lines' ,'Internet Service' ,'Online Backup' ,'Device Protection' ,'Tech Support' ,'Streaming TV' ,'Streaming Movies']
    
    hor_space = 0.02
    ver_space = 0.02
    
    fig = go.FigureWidget(make_subplots(rows=4,
                                        cols=4,
                                        specs=[[{'type':'domain'}, {'type':'domain'}, {'type':'domain'}, {'type':'domain'}],
                                               [{'type':'xy'}, {'type':'xy'}, {'type':'xy'}, {'type':'xy'}],
                                               [{'type':'domain'}, {'type':'domain'}, {'type':'domain'}, {'type':'domain'}],
                                               [{'type':'xy'}, {'type':'xy'}, {'type':'xy'}, {'type':'xy'}]
                                               ],
                                        horizontal_spacing=hor_space, # in range 0 to 1/(cols-1)
                                        vertical_spacing=ver_space # in range 0 to 1/(rows-1)
                                        )
                          )
    
    row, col = 1, 0
    for i, (title, data_col) in enumerate(zip(titles, data_cols)):
        row, col = divmod(i, 4)
        row = row * 2
        
        # Get value counts for pie chart
        value_counts = df[data_col].value_counts()
        # Create pie chart trace and add to subplot
        pie_chart = go.Pie(labels=value_counts.index, values=value_counts.to_numpy(), name=title, title=title)
        fig.add_trace(pie_chart, row=row+1, col=col+1)
     
        # get churn rates
        churn_counts = df.groupby([data_col, 'Churn'])['Churn'].count().unstack()
        # Create stacked bar charts
        t1 = go.Bar(name='Churn (yes)', x=churn_counts['Yes'].index, y=churn_counts['Yes'])
        t2 = go.Bar(name='Churn (no)', x=churn_counts['No'].index, y=churn_counts['No'], marker_color='indianred')
        fig.add_trace(t1, row=row+2, col=col+1)
        fig.add_trace(t2, row=row+2, col=col+1)
    
    
    fig.update_layout(title="Distribution of Customer Services",
                      barmode='stack',
                      showlegend=False,
                       margin={"l":25,
                               "r":25,
                               "t":25,
                               "b":25}
                      )
    fig.show()
    

    Some background provided by the plotly documentation: