ciaran macdermott
ciaran macdermott

Reputation: 1

Python Pptx stacked chart creation - add label for 'total bar'

I have not been able to figure out how to add a total bar label for a stacked chart here. Some suggestions included adding textboxes etc. but that has not resolved the issue so far. Any help is much appreciated and code is below:

def format_stacked_bar_chart(chart_placeholder, 
                             df_val, 
                             category_column, 
                             grouping_col,
                             value_column, 
                             proportion_column, 
                             dynamic_color_mapping,  # Use dynamic color mapping
                             ordered_formats, 
                             has_legend=False,
                             has_dollars=False):  # New argument for dollar formatting
    """
    Format and insert a stacked bar chart, using dynamic colors based on ranked proportions.
    """
    # Prepare the chart data for the stacked bar chart
    chart_data = CategoryChartData()

    # Ensure the formats/categories are ordered by proportion
    df_val[category_column] = pd.Categorical(df_val[category_column], categories=ordered_formats, ordered=True)

    # Sort by year and the custom order of the categories
    df_val = df_val.sort_values(by=[grouping_col, 'year', category_column], ascending=[True, True, True])

    # Add series for each format/category, using the actual sales amount for stacking by year
    chart_data.categories = df_val['year'].unique()

    for category in ordered_formats:
        if category in df_val[category_column].unique():  # Ensure the category exists in the current segment
            category_data = df_val[df_val[category_column] == category]
            actual_values_by_year = category_data.groupby([grouping_col, 'year'])[value_column].sum()
            chart_data.add_series(category, actual_values_by_year)

    # Insert the stacked bar chart
    chart_frame = chart_placeholder.insert_chart(XL_CHART_TYPE.COLUMN_STACKED, chart_data)
    stacked_bars = chart_frame.chart
    stacked_bars.has_title = False

    # Get the max value from the column to determine the axis formatting
    max_value = df_val[value_column].max()

    # Format the value axis (Y-axis) based on the maximum value in the data
    value_axis = stacked_bars.value_axis
    value_axis.format.line.fill.solid()
    value_axis.format.line.fill.fore_color.rgb = RGBColor(211, 211, 211)
    determine_scale_and_format_axis(value_axis, max_value, font_size=12, has_dollars=has_dollars)

    # Set the gap width to make bars wider
    stacked_bars.plots[0].gap_width = 52

    # Format the category axis (X-axis -> years)
    category_axis = stacked_bars.category_axis
    category_axis.tick_labels.font.size = Pt(12)
    category_axis.format.line.fill.solid()
    category_axis.format.line.fill.fore_color.rgb = RGBColor(211, 211, 211)
    category_axis.tick_label_position = XL_TICK_LABEL_POSITION.LOW
    category_axis.has_major_gridlines = False

    if has_legend:  
        chart_frame.width = edit_frame_width
        chart_frame.left = edit_frame_left 
        chart_frame.height = edit_frame_height 
        chart_frame.top = edit_frame_top 
    
        alterChartPlotAreaSize(stacked_bars, stack_x, stack_y, stack_w, stack_h)

        stacked_bars.has_legend = True 
        stacked_bars.legend.include_in_layout = False
        stacked_bars.legend.position = XL_LEGEND_POSITION.TOP
        stacked_bars.legend.font.size = Pt(12)
    else:
        stacked_bars.has_legend = False

    # Loop over series (formats/categories) to set colors and add labels only for significant proportions
    for series_idx, series in enumerate(stacked_bars.series):
        format_name = ordered_formats[series_idx]
        format_data_points = df_val[df_val[category_column] == format_name]

        for point_idx, point in enumerate(series.points):
            fill = point.format.fill
            fill.solid()

            # Apply dynamic color based on ranked proportion from dollars chart
            fill.fore_color.theme_color = dynamic_color_mapping.get(format_name, MSO_THEME_COLOR.ACCENT_6)  # Fallback color

            # Add data label only for significant proportions
            proportion_value = format_data_points[proportion_column].iloc[point_idx]
            if proportion_value > 0.1:  # Add labels only for proportions greater than 10%
                if has_dollars:
                    formatted_value = format_data(format_data_points['dollars_final'].iloc[point_idx],
                                                  takes_decimals=True, 
                                                  allow_billions=True, 
                                                  is_dollars=True)
                else:
                    formatted_value = format_data(format_data_points['units_final'].iloc[point_idx],
                                                  allow_billions=False)

                point.data_label.has_text_frame = True
                point.data_label.text_frame.text = formatted_value

                # Set font properties for the label
                for paragraph in point.data_label.text_frame.paragraphs:
                    for run in paragraph.runs:
                        run.font.size = Pt(12)
                        run.font.color.rgb = RGBColor(255, 255, 255)

                point.data_label.position = XL_DATA_LABEL_POSITION.CENTER

    return stacked_bars

I havn't found a solution yet and am not sure if this is even possible? Any help much appreciated

Upvotes: 0

Views: 13

Answers (0)

Related Questions