From 4bb0570fd48c5be2bc98ce895dfb394ae9ee3d68 Mon Sep 17 00:00:00 2001 From: Rob Letzler Date: Tue, 24 Dec 2024 22:59:41 -0500 Subject: [PATCH 1/7] Update bar-charts.md --- doc/python/bar-charts.md | 86 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/doc/python/bar-charts.md b/doc/python/bar-charts.md index 9ee75ea4406..6fad0f45c21 100644 --- a/doc/python/bar-charts.md +++ b/doc/python/bar-charts.md @@ -589,6 +589,92 @@ fig.update_layout( ) ``` +### Using a scatterplot to wrap long bars into multiple columns + +This 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. + +``` +import plotly.graph_objects as go +import pandas as pd + +#TODO: make the results and the code compellingly clear, terse, and well designed; for example, make sure all the variable names are descriptive +#TODO: when we're happy, remove print statements +#TODO: consider adding the value for each group either above its section or to its title + +def pictogram_bar(data, title, icon_size, max_height=10, units_per_icon=1,columns_between_units=.5): + fig = go.Figure() + + # Iterate through the data and create a scatter plot for each category + x_start = 1 + tick_locations = [] + for i, (category, count) in enumerate(data.items()): + #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 + count = round(count / units_per_icon) + num_cols = (count + max_height - 1) // max_height # Ceiling division + x_coordinates = [] + y_coordinates = [] + for col in range(num_cols): + print([x_start+col]*min(max_height, count-col*max_height)) + x_coordinates += [x_start+col]*min(max_height, count-col*max_height) + print(list(range(0, min(max_height, count-col*max_height)))) + for yc in range(1, min(max_height, count-col*max_height)+1): + y_coordinates.append(yc) + print(f"{category=}") + print(f"{x_coordinates=}") + print(f"{y_coordinates=}") + # Add dots for this category + fig.add_trace(go.Scatter( + x=x_coordinates, # All dots are in the same x position (category) + y=y_coordinates, + mode='markers', + marker=dict(size=icon_size, symbol="square", color=i), + name=category, + #text=[category] * (y_end - y_start), # Hover text + hoverinfo="text" + )) + tick_locations += [x_start+ (col)/2] + x_start += col+1+columns_between_units + print(f"{tick_locations=}") + + # Update layout for better visualization + fig.update_layout( + title=title, + xaxis=dict( + tickvals=tick_locations, + ticktext=list(data.keys()), + tickangle=-45, + showgrid=False + ), + #TODO: HIDE THE Y-AXIS? OR ENUMERATE IT IN "NATURAL UNITS" -- so count + yaxis=dict( + title="Units", + showgrid=False, + showline=False, + zeroline=False + ), + #TO DO: SHOW THE LEGEND, BUT JUST FOR ONE TRACE; LABEL IT WITH SOMETHING LIKE "EACH ICON REPRESENTS {units_per_icon} {Y_VARNAME}" + showlegend=False, + #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 + #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 + # 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 + height=600 + ) + + # Show the plot + fig.show() + +# TODO: CHANGE THIS THROUGHOUT TO A DF NAMED DF. + +data = { + "Haverford College": 1421, #https://www.usnews.com/best-colleges/haverford-college-3274 + "University of Mary Washington": 3611, #https://www.usnews.com/best-colleges/university-of-mary-washington-3746#:~:text=Overview,campus%20size%20is%20234%20acres. + "Brown University": 7226, #https://oir.brown.edu/institutional-data/factbooks/enrollment + "Arizona State University": 65174, #https://www.usnews.com/best-colleges/arizona-state-university-1081 +} + +pictogram_bar(data, title="Undergraduate Enrollment at Participating Schools", units_per_icon=1000, icon_size=27) +``` + ### Customizing Individual Bar Base ```python From a6a972d577ac7f478198b00d7d5bf3b8bd9c3987 Mon Sep 17 00:00:00 2001 From: SimaRaha Date: Wed, 8 Jan 2025 22:33:12 -0500 Subject: [PATCH 2/7] Add plotly-universities-1.ipynb for pictogram bar chart testing --- plotly-universities-1.ipynb | 1360 +++++++++++++++++++++++++++++++++++ 1 file changed, 1360 insertions(+) create mode 100644 plotly-universities-1.ipynb diff --git a/plotly-universities-1.ipynb b/plotly-universities-1.ipynb new file mode 100644 index 00000000000..dbbaddb98fe --- /dev/null +++ b/plotly-universities-1.ipynb @@ -0,0 +1,1360 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hoverinfo": "text", + "marker": { + "color": 0, + "size": 27, + "symbol": "square" + }, + "mode": "markers", + "name": "Haverford College", + "text": [ + "Haverford College: 1421" + ], + "type": "scatter", + "x": [ + 1 + ], + "y": [ + 1.05 + ] + }, + { + "mode": "text", + "showlegend": false, + "text": [ + "1421" + ], + "textfont": { + "color": "black", + "size": 14 + }, + "type": "scatter", + "x": [ + 1 + ], + "y": [ + 11 + ] + }, + { + "hoverinfo": "text", + "marker": { + "color": 1, + "size": 27, + "symbol": "square" + }, + "mode": "markers", + "name": "University of Mary Washington", + "text": [ + "University of Mary Washington: 3611", + "University of Mary Washington: 3611", + "University of Mary Washington: 3611", + "University of Mary Washington: 3611" + ], + "type": "scatter", + "x": [ + 2.005, + 2.005, + 2.005, + 2.005 + ], + "y": [ + 1.05, + 2.1, + 3.15, + 4.2 + ] + }, + { + "mode": "text", + "showlegend": false, + "text": [ + "3611" + ], + "textfont": { + "color": "black", + "size": 14 + }, + "type": "scatter", + "x": [ + 2.005 + ], + "y": [ + 11 + ] + }, + { + "hoverinfo": "text", + "marker": { + "color": 2, + "size": 27, + "symbol": "square" + }, + "mode": "markers", + "name": "Brown University", + "text": [ + "Brown University: 7226", + "Brown University: 7226", + "Brown University: 7226", + "Brown University: 7226", + "Brown University: 7226", + "Brown University: 7226", + "Brown University: 7226" + ], + "type": "scatter", + "x": [ + 3.01, + 3.01, + 3.01, + 3.01, + 3.01, + 3.01, + 3.01 + ], + "y": [ + 1.05, + 2.1, + 3.15, + 4.2, + 5.25, + 6.3, + 7.35 + ] + }, + { + "mode": "text", + "showlegend": false, + "text": [ + "7226" + ], + "textfont": { + "color": "black", + "size": 14 + }, + "type": "scatter", + "x": [ + 3.01 + ], + "y": [ + 11 + ] + }, + { + "hoverinfo": "text", + "marker": { + "color": 3, + "size": 27, + "symbol": "square" + }, + "mode": "markers", + "name": "Arizona State University", + "text": [ + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174", + "Arizona State University: 65174" + ], + "type": "scatter", + "x": [ + 4.015, + 4.015, + 4.015, + 4.015, + 4.015, + 4.015, + 4.015, + 4.015, + 4.015, + 4.015, + 5.015, + 5.015, + 5.015, + 5.015, + 5.015, + 5.015, + 5.015, + 5.015, + 5.015, + 5.015, + 6.015, + 6.015, + 6.015, + 6.015, + 6.015, + 6.015, + 6.015, + 6.015, + 6.015, + 6.015, + 7.015, + 7.015, + 7.015, + 7.015, + 7.015, + 7.015, + 7.015, + 7.015, + 7.015, + 7.015, + 8.015, + 8.015, + 8.015, + 8.015, + 8.015, + 8.015, + 8.015, + 8.015, + 8.015, + 8.015, + 9.015, + 9.015, + 9.015, + 9.015, + 9.015, + 9.015, + 9.015, + 9.015, + 9.015, + 9.015, + 10.015, + 10.015, + 10.015, + 10.015, + 10.015 + ], + "y": [ + 1.05, + 2.1, + 3.15, + 4.2, + 5.25, + 6.3, + 7.35, + 8.4, + 9.45, + 10.5, + 1.05, + 2.1, + 3.15, + 4.2, + 5.25, + 6.3, + 7.35, + 8.4, + 9.45, + 10.5, + 1.05, + 2.1, + 3.15, + 4.2, + 5.25, + 6.3, + 7.35, + 8.4, + 9.45, + 10.5, + 1.05, + 2.1, + 3.15, + 4.2, + 5.25, + 6.3, + 7.35, + 8.4, + 9.45, + 10.5, + 1.05, + 2.1, + 3.15, + 4.2, + 5.25, + 6.3, + 7.35, + 8.4, + 9.45, + 10.5, + 1.05, + 2.1, + 3.15, + 4.2, + 5.25, + 6.3, + 7.35, + 8.4, + 9.45, + 10.5, + 1.05, + 2.1, + 3.15, + 4.2, + 5.25 + ] + }, + { + "mode": "text", + "showlegend": false, + "text": [ + "65174" + ], + "textfont": { + "color": "black", + "size": 14 + }, + "type": "scatter", + "x": [ + 7.015 + ], + "y": [ + 11 + ] + } + ], + "layout": { + "height": 600, + "showlegend": false, + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "text": "Undergraduate Enrollment at Participating Schools" + }, + "width": 1000, + "xaxis": { + "showgrid": false, + "tickangle": -45, + "ticktext": [ + "Haverford College", + "University of Mary Washington", + "Brown University", + "Arizona State University" + ], + "tickvals": [ + 1, + 2.005, + 3.01, + 7.015 + ], + "title": { + "text": "Categories" + } + }, + "yaxis": { + "showgrid": false, + "title": { + "text": "Units (1 icon = 1000)" + }, + "zeroline": false + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "def pictogram_bar(data, title, icon_size, max_height=10, units_per_icon=1, column_spacing=0.005,icon_spacing=0.005):\n", + " \n", + " fig = go.Figure()\n", + " x_start = 1\n", + " tick_locations = []\n", + "\n", + " for i, (category, value) in enumerate(data.items()):\n", + " icon_count = round(value / units_per_icon)\n", + " num_columns = -(-icon_count // max_height) # Ceiling division\n", + "\n", + " x_coordinates, y_coordinates = [], []\n", + " for col in range(num_columns):\n", + " column_icons = min(max_height, icon_count - col * max_height)\n", + " x_coordinates.extend([x_start + col] * column_icons)\n", + " # y_coordinates.extend(range(1, column_icons + 1))\n", + " y_coordinates.extend([y + icon_spacing * y for y in range(1, column_icons + 1)])\n", + "\n", + "\n", + " # Add scatter plot for the category\n", + " fig.add_trace(go.Scatter(\n", + " x=x_coordinates,\n", + " y=y_coordinates,\n", + " mode='markers',\n", + " marker=dict(size=icon_size, symbol=\"square\", color= i),\n", + " name=category,\n", + " hoverinfo=\"text\",\n", + " text=[f\"{category}: {value}\" for _ in range(len(x_coordinates))]\n", + " ))\n", + " \n", + "\n", + " # Add value annotations above the section: new line of code \n", + " fig.add_trace(go.Scatter(\n", + " x=[x_start + (num_columns - 1) / 2],\n", + " y=[max_height + 1],\n", + " mode=\"text\",\n", + " text=[f\"{value}\"],\n", + " textfont=dict(size=14, color=\"black\"),\n", + " showlegend=False\n", + " ))\n", + "\n", + " # Track tick locations\n", + " tick_locations.append(x_start + (num_columns - 1) / 2)\n", + " x_start += num_columns + column_spacing\n", + "\n", + " # Update layout\n", + " fig.update_layout(\n", + " title=title,\n", + " xaxis=dict(\n", + " tickvals=tick_locations,\n", + " ticktext=list(data.keys()),\n", + " tickangle=-45,\n", + " showgrid=False,\n", + " title=\"Categories\"\n", + " ),\n", + " yaxis=dict(\n", + " title=f\"Units (1 icon = {units_per_icon})\",\n", + " showgrid=False,\n", + " zeroline=False,\n", + " ),\n", + " showlegend=False,\n", + " height=600,\n", + " width=(len(data) * 200 + 200) # Done adjused width based on number of categories\n", + " )\n", + "\n", + " fig.show()\n", + "\n", + "\n", + "# done as pd \n", + "df = pd.DataFrame({\n", + " 'School': [\"Haverford College\", \"University of Mary Washington\", \"Brown University\", \"Arizona State University\"],\n", + " 'Enrollment': [1421, 3611, 7226, 65174]\n", + "})\n", + "\n", + "pictogram_bar(\n", + " data={row['School']: row['Enrollment'] for _, row in df.iterrows()},\n", + " title=\"Undergraduate Enrollment at Participating Schools\",\n", + " units_per_icon=1000,\n", + " icon_size=27,\n", + " icon_spacing=0.05\n", + ")\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "3.11.9", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 0770689278e7f34fc5e1f7690f1418c97177d6c5 Mon Sep 17 00:00:00 2001 From: Rob Letzler <22990670+rl-utility-man@users.noreply.github.com> Date: Tue, 3 Jun 2025 13:28:23 -0400 Subject: [PATCH 3/7] Delete plotly-universities-1.ipynb --- plotly-universities-1.ipynb | 1360 ----------------------------------- 1 file changed, 1360 deletions(-) delete mode 100644 plotly-universities-1.ipynb diff --git a/plotly-universities-1.ipynb b/plotly-universities-1.ipynb deleted file mode 100644 index dbbaddb98fe..00000000000 --- a/plotly-universities-1.ipynb +++ /dev/null @@ -1,1360 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "hoverinfo": "text", - "marker": { - "color": 0, - "size": 27, - "symbol": "square" - }, - "mode": "markers", - "name": "Haverford College", - "text": [ - "Haverford College: 1421" - ], - "type": "scatter", - "x": [ - 1 - ], - "y": [ - 1.05 - ] - }, - { - "mode": "text", - "showlegend": false, - "text": [ - "1421" - ], - "textfont": { - "color": "black", - "size": 14 - }, - "type": "scatter", - "x": [ - 1 - ], - "y": [ - 11 - ] - }, - { - "hoverinfo": "text", - "marker": { - "color": 1, - "size": 27, - "symbol": "square" - }, - "mode": "markers", - "name": "University of Mary Washington", - "text": [ - "University of Mary Washington: 3611", - "University of Mary Washington: 3611", - "University of Mary Washington: 3611", - "University of Mary Washington: 3611" - ], - "type": "scatter", - "x": [ - 2.005, - 2.005, - 2.005, - 2.005 - ], - "y": [ - 1.05, - 2.1, - 3.15, - 4.2 - ] - }, - { - "mode": "text", - "showlegend": false, - "text": [ - "3611" - ], - "textfont": { - "color": "black", - "size": 14 - }, - "type": "scatter", - "x": [ - 2.005 - ], - "y": [ - 11 - ] - }, - { - "hoverinfo": "text", - "marker": { - "color": 2, - "size": 27, - "symbol": "square" - }, - "mode": "markers", - "name": "Brown University", - "text": [ - "Brown University: 7226", - "Brown University: 7226", - "Brown University: 7226", - "Brown University: 7226", - "Brown University: 7226", - "Brown University: 7226", - "Brown University: 7226" - ], - "type": "scatter", - "x": [ - 3.01, - 3.01, - 3.01, - 3.01, - 3.01, - 3.01, - 3.01 - ], - "y": [ - 1.05, - 2.1, - 3.15, - 4.2, - 5.25, - 6.3, - 7.35 - ] - }, - { - "mode": "text", - "showlegend": false, - "text": [ - "7226" - ], - "textfont": { - "color": "black", - "size": 14 - }, - "type": "scatter", - "x": [ - 3.01 - ], - "y": [ - 11 - ] - }, - { - "hoverinfo": "text", - "marker": { - "color": 3, - "size": 27, - "symbol": "square" - }, - "mode": "markers", - "name": "Arizona State University", - "text": [ - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174", - "Arizona State University: 65174" - ], - "type": "scatter", - "x": [ - 4.015, - 4.015, - 4.015, - 4.015, - 4.015, - 4.015, - 4.015, - 4.015, - 4.015, - 4.015, - 5.015, - 5.015, - 5.015, - 5.015, - 5.015, - 5.015, - 5.015, - 5.015, - 5.015, - 5.015, - 6.015, - 6.015, - 6.015, - 6.015, - 6.015, - 6.015, - 6.015, - 6.015, - 6.015, - 6.015, - 7.015, - 7.015, - 7.015, - 7.015, - 7.015, - 7.015, - 7.015, - 7.015, - 7.015, - 7.015, - 8.015, - 8.015, - 8.015, - 8.015, - 8.015, - 8.015, - 8.015, - 8.015, - 8.015, - 8.015, - 9.015, - 9.015, - 9.015, - 9.015, - 9.015, - 9.015, - 9.015, - 9.015, - 9.015, - 9.015, - 10.015, - 10.015, - 10.015, - 10.015, - 10.015 - ], - "y": [ - 1.05, - 2.1, - 3.15, - 4.2, - 5.25, - 6.3, - 7.35, - 8.4, - 9.45, - 10.5, - 1.05, - 2.1, - 3.15, - 4.2, - 5.25, - 6.3, - 7.35, - 8.4, - 9.45, - 10.5, - 1.05, - 2.1, - 3.15, - 4.2, - 5.25, - 6.3, - 7.35, - 8.4, - 9.45, - 10.5, - 1.05, - 2.1, - 3.15, - 4.2, - 5.25, - 6.3, - 7.35, - 8.4, - 9.45, - 10.5, - 1.05, - 2.1, - 3.15, - 4.2, - 5.25, - 6.3, - 7.35, - 8.4, - 9.45, - 10.5, - 1.05, - 2.1, - 3.15, - 4.2, - 5.25, - 6.3, - 7.35, - 8.4, - 9.45, - 10.5, - 1.05, - 2.1, - 3.15, - 4.2, - 5.25 - ] - }, - { - "mode": "text", - "showlegend": false, - "text": [ - "65174" - ], - "textfont": { - "color": "black", - "size": 14 - }, - "type": "scatter", - "x": [ - 7.015 - ], - "y": [ - 11 - ] - } - ], - "layout": { - "height": 600, - "showlegend": false, - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - }, - "title": { - "text": "Undergraduate Enrollment at Participating Schools" - }, - "width": 1000, - "xaxis": { - "showgrid": false, - "tickangle": -45, - "ticktext": [ - "Haverford College", - "University of Mary Washington", - "Brown University", - "Arizona State University" - ], - "tickvals": [ - 1, - 2.005, - 3.01, - 7.015 - ], - "title": { - "text": "Categories" - } - }, - "yaxis": { - "showgrid": false, - "title": { - "text": "Units (1 icon = 1000)" - }, - "zeroline": false - } - } - } - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import plotly.graph_objects as go\n", - "import pandas as pd\n", - "\n", - "def pictogram_bar(data, title, icon_size, max_height=10, units_per_icon=1, column_spacing=0.005,icon_spacing=0.005):\n", - " \n", - " fig = go.Figure()\n", - " x_start = 1\n", - " tick_locations = []\n", - "\n", - " for i, (category, value) in enumerate(data.items()):\n", - " icon_count = round(value / units_per_icon)\n", - " num_columns = -(-icon_count // max_height) # Ceiling division\n", - "\n", - " x_coordinates, y_coordinates = [], []\n", - " for col in range(num_columns):\n", - " column_icons = min(max_height, icon_count - col * max_height)\n", - " x_coordinates.extend([x_start + col] * column_icons)\n", - " # y_coordinates.extend(range(1, column_icons + 1))\n", - " y_coordinates.extend([y + icon_spacing * y for y in range(1, column_icons + 1)])\n", - "\n", - "\n", - " # Add scatter plot for the category\n", - " fig.add_trace(go.Scatter(\n", - " x=x_coordinates,\n", - " y=y_coordinates,\n", - " mode='markers',\n", - " marker=dict(size=icon_size, symbol=\"square\", color= i),\n", - " name=category,\n", - " hoverinfo=\"text\",\n", - " text=[f\"{category}: {value}\" for _ in range(len(x_coordinates))]\n", - " ))\n", - " \n", - "\n", - " # Add value annotations above the section: new line of code \n", - " fig.add_trace(go.Scatter(\n", - " x=[x_start + (num_columns - 1) / 2],\n", - " y=[max_height + 1],\n", - " mode=\"text\",\n", - " text=[f\"{value}\"],\n", - " textfont=dict(size=14, color=\"black\"),\n", - " showlegend=False\n", - " ))\n", - "\n", - " # Track tick locations\n", - " tick_locations.append(x_start + (num_columns - 1) / 2)\n", - " x_start += num_columns + column_spacing\n", - "\n", - " # Update layout\n", - " fig.update_layout(\n", - " title=title,\n", - " xaxis=dict(\n", - " tickvals=tick_locations,\n", - " ticktext=list(data.keys()),\n", - " tickangle=-45,\n", - " showgrid=False,\n", - " title=\"Categories\"\n", - " ),\n", - " yaxis=dict(\n", - " title=f\"Units (1 icon = {units_per_icon})\",\n", - " showgrid=False,\n", - " zeroline=False,\n", - " ),\n", - " showlegend=False,\n", - " height=600,\n", - " width=(len(data) * 200 + 200) # Done adjused width based on number of categories\n", - " )\n", - "\n", - " fig.show()\n", - "\n", - "\n", - "# done as pd \n", - "df = pd.DataFrame({\n", - " 'School': [\"Haverford College\", \"University of Mary Washington\", \"Brown University\", \"Arizona State University\"],\n", - " 'Enrollment': [1421, 3611, 7226, 65174]\n", - "})\n", - "\n", - "pictogram_bar(\n", - " data={row['School']: row['Enrollment'] for _, row in df.iterrows()},\n", - " title=\"Undergraduate Enrollment at Participating Schools\",\n", - " units_per_icon=1000,\n", - " icon_size=27,\n", - " icon_spacing=0.05\n", - ")\n", - "\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "3.11.9", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.4" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} From 85bd8efbf0c9e5864791a906630f8e43aaa9eac8 Mon Sep 17 00:00:00 2001 From: Rob Letzler <22990670+rl-utility-man@users.noreply.github.com> Date: Tue, 3 Jun 2025 13:53:19 -0400 Subject: [PATCH 4/7] moved the revised code into bar-charts.md from the notebook --- doc/python/bar-charts.md | 104 ++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 51 deletions(-) diff --git a/doc/python/bar-charts.md b/doc/python/bar-charts.md index 6fad0f45c21..3c9819a5a82 100644 --- a/doc/python/bar-charts.md +++ b/doc/python/bar-charts.md @@ -597,82 +597,84 @@ This bar-style pictogram allows readers to focus on the relative sizes of smalle import plotly.graph_objects as go import pandas as pd -#TODO: make the results and the code compellingly clear, terse, and well designed; for example, make sure all the variable names are descriptive -#TODO: when we're happy, remove print statements -#TODO: consider adding the value for each group either above its section or to its title - -def pictogram_bar(data, title, icon_size, max_height=10, units_per_icon=1,columns_between_units=.5): - fig = go.Figure() +def pictogram_bar(data, title, icon_size, max_height=10, units_per_icon=1, column_spacing=.75,icon_spacing=0.005): - # Iterate through the data and create a scatter plot for each category + fig = go.Figure() x_start = 1 tick_locations = [] - for i, (category, count) in enumerate(data.items()): - #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 - count = round(count / units_per_icon) - num_cols = (count + max_height - 1) // max_height # Ceiling division - x_coordinates = [] - y_coordinates = [] - for col in range(num_cols): - print([x_start+col]*min(max_height, count-col*max_height)) - x_coordinates += [x_start+col]*min(max_height, count-col*max_height) - print(list(range(0, min(max_height, count-col*max_height)))) - for yc in range(1, min(max_height, count-col*max_height)+1): - y_coordinates.append(yc) - print(f"{category=}") - print(f"{x_coordinates=}") - print(f"{y_coordinates=}") - # Add dots for this category + + for i, (category, value) in enumerate(data.items()): + icon_count = round(value / units_per_icon) + num_columns = -(-icon_count // max_height) # Ceiling division + + x_coordinates, y_coordinates = [], [] + for col in range(num_columns): + column_icons = min(max_height, icon_count - col * max_height) + x_coordinates.extend([x_start + col] * column_icons) + y_coordinates.extend([y + icon_spacing * y for y in range(1, column_icons + 1)]) + + + # Add scatter plot for the category fig.add_trace(go.Scatter( - x=x_coordinates, # All dots are in the same x position (category) + x=x_coordinates, y=y_coordinates, mode='markers', - marker=dict(size=icon_size, symbol="square", color=i), + marker=dict(size=icon_size, symbol="square", color= i), name=category, - #text=[category] * (y_end - y_start), # Hover text - hoverinfo="text" + hoverinfo="text", + text=[f"{category}: {value}" for _ in range(len(x_coordinates))] )) - tick_locations += [x_start+ (col)/2] - x_start += col+1+columns_between_units - print(f"{tick_locations=}") - - # Update layout for better visualization + + + # Add value annotations above the section + fig.add_trace(go.Scatter( + x=[x_start + (num_columns - 1) / 2], + y=[max_height + 1.2], + mode="text", + text=[f"{value}"], + textfont=dict(size=14, color="black"), + showlegend=False + )) + + # Track tick locations + tick_locations.append(x_start + (num_columns - 1) / 2) + x_start += num_columns + column_spacing + + # Update layout fig.update_layout( title=title, xaxis=dict( tickvals=tick_locations, ticktext=list(data.keys()), tickangle=-45, - showgrid=False + showgrid=False, + title="Categories" ), - #TODO: HIDE THE Y-AXIS? OR ENUMERATE IT IN "NATURAL UNITS" -- so count yaxis=dict( - title="Units", + title=f"Units (1 icon = {units_per_icon})", showgrid=False, - showline=False, - zeroline=False + zeroline=False, ), - #TO DO: SHOW THE LEGEND, BUT JUST FOR ONE TRACE; LABEL IT WITH SOMETHING LIKE "EACH ICON REPRESENTS {units_per_icon} {Y_VARNAME}" showlegend=False, - #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 - #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 - # 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 - height=600 + height=600, + width=(len(data) * 200 + 200) ) - # Show the plot fig.show() -# TODO: CHANGE THIS THROUGHOUT TO A DF NAMED DF. -data = { - "Haverford College": 1421, #https://www.usnews.com/best-colleges/haverford-college-3274 - "University of Mary Washington": 3611, #https://www.usnews.com/best-colleges/university-of-mary-washington-3746#:~:text=Overview,campus%20size%20is%20234%20acres. - "Brown University": 7226, #https://oir.brown.edu/institutional-data/factbooks/enrollment - "Arizona State University": 65174, #https://www.usnews.com/best-colleges/arizona-state-university-1081 -} +df = pd.DataFrame({ + 'School': ["Haverford College", "University of Mary Washington", "Brown University", "Arizona State University"], + 'Enrollment': [1421, 3611, 7226, 65174] +}) -pictogram_bar(data, title="Undergraduate Enrollment at Participating Schools", units_per_icon=1000, icon_size=27) +pictogram_bar( + data={row['School']: row['Enrollment'] for _, row in df.iterrows()}, + title="Undergraduate Enrollment at Participating Schools", + units_per_icon=1000, + icon_size=27, + icon_spacing=0.05 +) ``` ### Customizing Individual Bar Base From 595172c0ad6c3bdb88649e8126b51fc105130833 Mon Sep 17 00:00:00 2001 From: Rob Letzler <22990670+rl-utility-man@users.noreply.github.com> Date: Wed, 4 Jun 2025 22:54:31 -0400 Subject: [PATCH 5/7] added lots of comments; adjusted layout --- doc/python/bar-charts.md | 60 +++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/doc/python/bar-charts.md b/doc/python/bar-charts.md index 3c9819a5a82..d2b9c6bac6c 100644 --- a/doc/python/bar-charts.md +++ b/doc/python/bar-charts.md @@ -593,27 +593,37 @@ fig.update_layout( This 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. -``` +```python import plotly.graph_objects as go import pandas as pd - -def pictogram_bar(data, title, icon_size, max_height=10, units_per_icon=1, column_spacing=.75,icon_spacing=0.005): +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): fig = go.Figure() x_start = 1 tick_locations = [] - + #loop through each group and create a trace with its icons for i, (category, value) in enumerate(data.items()): + # compute the number of icons represent this category icon_count = round(value / units_per_icon) - num_columns = -(-icon_count // max_height) # Ceiling division - + # compute the number of columns in which to arrange this category + # every category gets at least one column; we use integer division + # to compute the number of additional columns + num_columns = (icon_count // max_icons_per_column)+1 + + #create lists of coordinates and populate them x_coordinates, y_coordinates = [], [] for col in range(num_columns): - column_icons = min(max_height, icon_count - col * max_height) + # the number of icons in this column is the lesser of the column height or + # the number of icons remaining to place + column_icons = min(max_icons_per_column, icon_count - col * max_icons_per_column) + + # create a list element containing the x-coordinate of this column; + # add column_icons copies of that coordinate to the list of icon x coordinates + # normalizing the width of each within group column to 1 simplifies the code + # we can adjust the visible space between columns by adjusting the total width below x_coordinates.extend([x_start + col] * column_icons) - y_coordinates.extend([y + icon_spacing * y for y in range(1, column_icons + 1)]) - - + # create a list of sequentially increasing y-coordinates for icons + y_coordinates.extend([y + icon_vertical_spacing * y for y in range(1, column_icons + 1)]) # Add scatter plot for the category fig.add_trace(go.Scatter( x=x_coordinates, @@ -621,25 +631,24 @@ def pictogram_bar(data, title, icon_size, max_height=10, units_per_icon=1, colum mode='markers', marker=dict(size=icon_size, symbol="square", color= i), name=category, + #suppress the x and y coordinates in the hover text, since they are meaningless to readers hoverinfo="text", text=[f"{category}: {value}" for _ in range(len(x_coordinates))] )) - - # Add value annotations above the section + # add an annotation above the center of each section showing its value fig.add_trace(go.Scatter( - x=[x_start + (num_columns - 1) / 2], - y=[max_height + 1.2], + x=[x_start + (num_columns - 1) / 2], #center + y=[max_icons_per_column + 1.2], mode="text", text=[f"{value}"], textfont=dict(size=14, color="black"), showlegend=False )) - - # Track tick locations + # Track locations where we will put the text for each category tick_locations.append(x_start + (num_columns - 1) / 2) - x_start += num_columns + column_spacing - + #compute the left edge of the next category + x_start += num_columns + inter_group_spacing # Update layout fig.update_layout( title=title, @@ -651,18 +660,18 @@ def pictogram_bar(data, title, icon_size, max_height=10, units_per_icon=1, colum title="Categories" ), yaxis=dict( - title=f"Units (1 icon = {units_per_icon})", + title=f"Units (1 icon = {units_per_icon:,g} {unit_description})", showgrid=False, zeroline=False, ), - showlegend=False, - height=600, - width=(len(data) * 200 + 200) + # we've got all the labeling we need without a legend + showlegend=False, + height=700, + # the x-coordinates get scales to fill available space, so adjusting the width is a good way to adjust spacing between columns + width=(len(data) * 150 + 50) ) - fig.show() - df = pd.DataFrame({ 'School': ["Haverford College", "University of Mary Washington", "Brown University", "Arizona State University"], 'Enrollment': [1421, 3611, 7226, 65174] @@ -672,8 +681,9 @@ pictogram_bar( data={row['School']: row['Enrollment'] for _, row in df.iterrows()}, title="Undergraduate Enrollment at Participating Schools", units_per_icon=1000, + unit_description = "students", icon_size=27, - icon_spacing=0.05 + icon_vertical_spacing=0.05 ) ``` From fbbae167aa48b9bc049f14c8889e90fd1be86f80 Mon Sep 17 00:00:00 2001 From: Rob Letzler <22990670+rl-utility-man@users.noreply.github.com> Date: Wed, 4 Jun 2025 22:56:00 -0400 Subject: [PATCH 6/7] Update bar-charts.md --- doc/python/bar-charts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/python/bar-charts.md b/doc/python/bar-charts.md index d2b9c6bac6c..8d7e1c09be4 100644 --- a/doc/python/bar-charts.md +++ b/doc/python/bar-charts.md @@ -667,7 +667,7 @@ def pictogram_bar(data, title, icon_size, max_icons_per_column=10, units_per_ico # we've got all the labeling we need without a legend showlegend=False, height=700, - # the x-coordinates get scales to fill available space, so adjusting the width is a good way to adjust spacing between columns + # the x-coordinates scale to fill available space, so adjusting the width of the image is a good way to adjust spacing between columns width=(len(data) * 150 + 50) ) fig.show() From 36345ce7b7fdde12be521a6f09b66bfc3adb26bf Mon Sep 17 00:00:00 2001 From: Rob Letzler <22990670+rl-utility-man@users.noreply.github.com> Date: Thu, 5 Jun 2025 12:45:20 -0400 Subject: [PATCH 7/7] fixed minor bugs and improved comments. --- doc/python/bar-charts.md | 44 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/doc/python/bar-charts.md b/doc/python/bar-charts.md index 8d7e1c09be4..39f40ae8a73 100644 --- a/doc/python/bar-charts.md +++ b/doc/python/bar-charts.md @@ -603,26 +603,25 @@ def pictogram_bar(data, title, icon_size, max_icons_per_column=10, units_per_ico tick_locations = [] #loop through each group and create a trace with its icons for i, (category, value) in enumerate(data.items()): - # compute the number of icons represent this category + # compute the number of icons to use to represent this category. Depending on your use case, you might replace round with floor or ceiling. icon_count = round(value / units_per_icon) - # compute the number of columns in which to arrange this category - # every category gets at least one column; we use integer division - # to compute the number of additional columns - num_columns = (icon_count // max_icons_per_column)+1 + # compute the number of columns in which to arrange the icons for this category + # using a double negative sign to convert a floor(division) operation into a ceiling(division) operation + num_columns = -(-icon_count // max_icons_per_column) - #create lists of coordinates and populate them + #create and populate lists of icon coordinates x_coordinates, y_coordinates = [], [] for col in range(num_columns): # the number of icons in this column is the lesser of the column height or # the number of icons remaining to place column_icons = min(max_icons_per_column, icon_count - col * max_icons_per_column) - # create a list element containing the x-coordinate of this column; - # add column_icons copies of that coordinate to the list of icon x coordinates - # normalizing the width of each within group column to 1 simplifies the code - # we can adjust the visible space between columns by adjusting the total width below + # Create a one item list containing the x-coordinate of this column. + # Then add column_icons copies of that coordinate to the list of icon x coordinates using list multiplication. + # Normalizing the width of each within-category column to 1 simplifies the code. + # We can adjust the visible space between columns by adjusting the total width below. x_coordinates.extend([x_start + col] * column_icons) - # create a list of sequentially increasing y-coordinates for icons + # Create a list of sequentially increasing y-coordinates for icons. y_coordinates.extend([y + icon_vertical_spacing * y for y in range(1, column_icons + 1)]) # Add scatter plot for the category fig.add_trace(go.Scatter( @@ -631,43 +630,48 @@ def pictogram_bar(data, title, icon_size, max_icons_per_column=10, units_per_ico mode='markers', marker=dict(size=icon_size, symbol="square", color= i), name=category, - #suppress the x and y coordinates in the hover text, since they are meaningless to readers + # Suppress the x and y coordinates in the hover text, since they are irrelevant implementation details. hoverinfo="text", text=[f"{category}: {value}" for _ in range(len(x_coordinates))] )) - # add an annotation above the center of each section showing its value + # Add an annotation above the center of each category showing its value fig.add_trace(go.Scatter( - x=[x_start + (num_columns - 1) / 2], #center - y=[max_icons_per_column + 1.2], + x=[x_start + (num_columns - 1) / 2], # Compute the location of the center + y=[max_icons_per_column* (1+icon_vertical_spacing) + 1.15], mode="text", text=[f"{value}"], textfont=dict(size=14, color="black"), showlegend=False )) - # Track locations where we will put the text for each category + # Track locations where we will put the text labeling each category tick_locations.append(x_start + (num_columns - 1) / 2) #compute the left edge of the next category x_start += num_columns + inter_group_spacing - # Update layout + fig.update_layout( title=title, xaxis=dict( tickvals=tick_locations, + # Label ecah category ticktext=list(data.keys()), tickangle=-45, showgrid=False, title="Categories" ), yaxis=dict( - title=f"Units (1 icon = {units_per_icon:,g} {unit_description})", + title=f"Each icon represents {units_per_icon:,g} {unit_description}", + # The y-axis goes above the top icon to make room for the annotations. + # We set tick values so the axis labeling does not go above the top icon. + # If you choose a value of max_icons_per_column that is not a multiple of 5, consider changing this. + tickvals=list(range(0,max_icons_per_column+1,5)), showgrid=False, zeroline=False, ), - # we've got all the labeling we need without a legend + # We have already got all the labeling we need so we suppress the legend. showlegend=False, height=700, - # the x-coordinates scale to fill available space, so adjusting the width of the image is a good way to adjust spacing between columns + # The x-coordinates scale to fill available space, so adjusting the width of the image is a good way to adjust spacing between columns. width=(len(data) * 150 + 50) ) fig.show()