Reputation: 1
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