@@ -597,82 +597,84 @@ This bar-style pictogram allows readers to focus on the relative sizes of smalle
597
597
import plotly.graph_objects as go
598
598
import pandas as pd
599
599
600
- #TODO: make the results and the code compellingly clear, terse, and well designed; for example, make sure all the variable names are descriptive
601
- #TODO: when we're happy, remove print statements
602
- #TODO: consider adding the value for each group either above its section or to its title
603
-
604
- def pictogram_bar(data, title, icon_size, max_height=10, units_per_icon=1,columns_between_units=.5):
605
- fig = go.Figure()
600
+ def pictogram_bar(data, title, icon_size, max_height=10, units_per_icon=1, column_spacing=.75,icon_spacing=0.005):
606
601
607
- # Iterate through the data and create a scatter plot for each category
602
+ fig = go.Figure()
608
603
x_start = 1
609
604
tick_locations = []
610
- for i, (category, count) in enumerate(data.items()):
611
- #convert the real number input to an integer number of icons. Depending on the context, you might want to take floor or a ceiling rather than rouding
612
- count = round(count / units_per_icon)
613
- num_cols = (count + max_height - 1) // max_height # Ceiling division
614
- x_coordinates = []
615
- y_coordinates = []
616
- for col in range(num_cols):
617
- print([x_start+col]*min(max_height, count-col*max_height))
618
- x_coordinates += [x_start+col]*min(max_height, count-col*max_height)
619
- print(list(range(0, min(max_height, count-col*max_height))))
620
- for yc in range(1, min(max_height, count-col*max_height)+1):
621
- y_coordinates.append(yc)
622
- print(f"{category=}")
623
- print(f"{x_coordinates=}")
624
- print(f"{y_coordinates=}")
625
- # Add dots for this category
605
+
606
+ for i, (category, value) in enumerate(data.items()):
607
+ icon_count = round(value / units_per_icon)
608
+ num_columns = -(-icon_count // max_height) # Ceiling division
609
+
610
+ x_coordinates, y_coordinates = [], []
611
+ for col in range(num_columns):
612
+ column_icons = min(max_height, icon_count - col * max_height)
613
+ 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
+
617
+ # Add scatter plot for the category
626
618
fig.add_trace(go.Scatter(
627
- x=x_coordinates, # All dots are in the same x position (category)
619
+ x=x_coordinates,
628
620
y=y_coordinates,
629
621
mode='markers',
630
- marker=dict(size=icon_size, symbol="square", color=i),
622
+ marker=dict(size=icon_size, symbol="square", color= i),
631
623
name=category,
632
- #text=[category] * (y_end - y_start), # Hover text
633
- hoverinfo="text"
624
+ hoverinfo=" text",
625
+ text=[f"{category}: {value}" for _ in range(len(x_coordinates))]
634
626
))
635
- tick_locations += [x_start+ (col)/2]
636
- x_start += col+1+columns_between_units
637
- print(f"{tick_locations=}")
638
-
639
- # Update layout for better visualization
627
+
628
+
629
+ # Add value annotations above the section
630
+ fig.add_trace(go.Scatter(
631
+ x=[x_start + (num_columns - 1) / 2],
632
+ y=[max_height + 1.2],
633
+ mode="text",
634
+ text=[f"{value}"],
635
+ textfont=dict(size=14, color="black"),
636
+ showlegend=False
637
+ ))
638
+
639
+ # Track tick locations
640
+ tick_locations.append(x_start + (num_columns - 1) / 2)
641
+ x_start += num_columns + column_spacing
642
+
643
+ # Update layout
640
644
fig.update_layout(
641
645
title=title,
642
646
xaxis=dict(
643
647
tickvals=tick_locations,
644
648
ticktext=list(data.keys()),
645
649
tickangle=-45,
646
- showgrid=False
650
+ showgrid=False,
651
+ title="Categories"
647
652
),
648
- #TODO: HIDE THE Y-AXIS? OR ENUMERATE IT IN "NATURAL UNITS" -- so count
649
653
yaxis=dict(
650
- title="Units",
654
+ title=f "Units (1 icon = {units_per_icon}) ",
651
655
showgrid=False,
652
- showline=False,
653
- zeroline=False
656
+ zeroline=False,
654
657
),
655
- #TO DO: SHOW THE LEGEND, BUT JUST FOR ONE TRACE; LABEL IT WITH SOMETHING LIKE "EACH ICON REPRESENTS {units_per_icon} {Y_VARNAME}"
656
658
showlegend=False,
657
- #setting the width implicitly sets the amount of space between columns within groups and it's desirable to keep those columns close but not too close
658
- #TODO: set the width to a value that makes the spacing between columns reasonable; try it as a function of the number of columns of data, number of columns left blank as spacers, the icon size; and the left and right margins
659
- # there's no right answer; but some answers will look a lot better than others; I'm guessing that roughly 2-3 times as many px as we fill with icons might be good
660
- height=600
659
+ height=600,
660
+ width=(len(data) * 200 + 200)
661
661
)
662
662
663
- # Show the plot
664
663
fig.show()
665
664
666
- # TODO: CHANGE THIS THROUGHOUT TO A DF NAMED DF.
667
665
668
- data = {
669
- "Haverford College": 1421, #https://www.usnews.com/best-colleges/haverford-college-3274
670
- "University of Mary Washington": 3611, #https://www.usnews.com/best-colleges/university-of-mary-washington-3746#:~:text=Overview,campus%20size%20is%20234%20acres.
671
- "Brown University": 7226, #https://oir.brown.edu/institutional-data/factbooks/enrollment
672
- "Arizona State University": 65174, #https://www.usnews.com/best-colleges/arizona-state-university-1081
673
- }
666
+ df = pd.DataFrame({
667
+ 'School': ["Haverford College", "University of Mary Washington", "Brown University", "Arizona State University"],
668
+ 'Enrollment': [1421, 3611, 7226, 65174]
669
+ })
674
670
675
- pictogram_bar(data, title="Undergraduate Enrollment at Participating Schools", units_per_icon=1000, icon_size=27)
671
+ pictogram_bar(
672
+ data={row['School']: row['Enrollment'] for _, row in df.iterrows()},
673
+ title="Undergraduate Enrollment at Participating Schools",
674
+ units_per_icon=1000,
675
+ icon_size=27,
676
+ icon_spacing=0.05
677
+ )
676
678
```
677
679
678
680
### Customizing Individual Bar Base
0 commit comments