@@ -593,53 +593,62 @@ fig.update_layout(
593593
594594This bar-style pictogram allows readers to focus on the relative sizes of smaller entities by wrapping the bar for largest entries into multiple columns. You could make it even more of a pictogram by using fontawesome to replace the square markers we use below with icons like mortar boards for students.
595595
596- ```
596+ ``` python
597597import plotly.graph_objects as go
598598import pandas as pd
599-
600- def pictogram_bar(data, title, icon_size, max_height=10, units_per_icon=1, column_spacing=.75,icon_spacing=0.005):
599+ def pictogram_bar (data , title , icon_size , max_icons_per_column = 10 , units_per_icon = 1 , unit_description = " " , inter_group_spacing = .8 ,icon_vertical_spacing = 0.005 ):
601600
602601 fig = go.Figure()
603602 x_start = 1
604603 tick_locations = []
605-
604+ # loop through each group and create a trace with its icons
606605 for i, (category, value) in enumerate (data.items()):
606+ # compute the number of icons represent this category
607607 icon_count = round (value / units_per_icon)
608- num_columns = -(-icon_count // max_height) # Ceiling division
609-
608+ # compute the number of columns in which to arrange this category
609+ # every category gets at least one column; we use integer division
610+ # to compute the number of additional columns
611+ num_columns = (icon_count // max_icons_per_column)+ 1
612+
613+ # create lists of coordinates and populate them
610614 x_coordinates, y_coordinates = [], []
611615 for col in range (num_columns):
612- column_icons = min(max_height, icon_count - col * max_height)
616+ # the number of icons in this column is the lesser of the column height or
617+ # the number of icons remaining to place
618+ column_icons = min (max_icons_per_column, icon_count - col * max_icons_per_column)
619+
620+ # create a list element containing the x-coordinate of this column;
621+ # add column_icons copies of that coordinate to the list of icon x coordinates
622+ # normalizing the width of each within group column to 1 simplifies the code
623+ # we can adjust the visible space between columns by adjusting the total width below
613624 x_coordinates.extend([x_start + col] * column_icons)
614- y_coordinates.extend([y + icon_spacing * y for y in range(1, column_icons + 1)])
615-
616-
625+ # create a list of sequentially increasing y-coordinates for icons
626+ y_coordinates.extend([y + icon_vertical_spacing * y for y in range (1 , column_icons + 1 )])
617627 # Add scatter plot for the category
618628 fig.add_trace(go.Scatter(
619629 x = x_coordinates,
620630 y = y_coordinates,
621631 mode = ' markers' ,
622632 marker = dict (size = icon_size, symbol = " square" , color = i),
623633 name = category,
634+ # suppress the x and y coordinates in the hover text, since they are meaningless to readers
624635 hoverinfo = " text" ,
625636 text = [f " { category} : { value} " for _ in range (len (x_coordinates))]
626637 ))
627638
628-
629- # Add value annotations above the section
639+ # add an annotation above the center of each section showing its value
630640 fig.add_trace(go.Scatter(
631- x=[x_start + (num_columns - 1) / 2],
632- y=[max_height + 1.2],
641+ x = [x_start + (num_columns - 1 ) / 2 ], # center
642+ y = [max_icons_per_column + 1.2 ],
633643 mode = " text" ,
634644 text = [f " { value} " ],
635645 textfont = dict (size = 14 , color = " black" ),
636646 showlegend = False
637647 ))
638-
639- # Track tick locations
648+ # Track locations where we will put the text for each category
640649 tick_locations.append(x_start + (num_columns - 1 ) / 2 )
641- x_start += num_columns + column_spacing
642-
650+ # compute the left edge of the next category
651+ x_start += num_columns + inter_group_spacing
643652 # Update layout
644653 fig.update_layout(
645654 title = title,
@@ -651,18 +660,18 @@ def pictogram_bar(data, title, icon_size, max_height=10, units_per_icon=1, colum
651660 title = " Categories"
652661 ),
653662 yaxis = dict (
654- title=f"Units (1 icon = {units_per_icon})",
663+ title = f " Units (1 icon = { units_per_icon:,g } { unit_description } ) " ,
655664 showgrid = False ,
656665 zeroline = False ,
657666 ),
658- showlegend=False,
659- height=600,
660- width=(len(data) * 200 + 200)
667+ # we've got all the labeling we need without a legend
668+ showlegend = False ,
669+ height = 700 ,
670+ # the x-coordinates get scales to fill available space, so adjusting the width is a good way to adjust spacing between columns
671+ width = (len (data) * 150 + 50 )
661672 )
662-
663673 fig.show()
664674
665-
666675df = pd.DataFrame({
667676 ' School' : [" Haverford College" , " University of Mary Washington" , " Brown University" , " Arizona State University" ],
668677 ' Enrollment' : [1421 , 3611 , 7226 , 65174 ]
@@ -672,8 +681,9 @@ pictogram_bar(
672681 data = {row[' School' ]: row[' Enrollment' ] for _, row in df.iterrows()},
673682 title = " Undergraduate Enrollment at Participating Schools" ,
674683 units_per_icon = 1000 ,
684+ unit_description = " students" ,
675685 icon_size = 27 ,
676- icon_spacing =0.05
686+ icon_vertical_spacing = 0.05
677687)
678688```
679689
0 commit comments