From 3cec78924e9daa99cb1c46bbacc4dbd2c283d05c Mon Sep 17 00:00:00 2001 From: Ace Pace Date: Sat, 21 Dec 2024 21:27:24 +0200 Subject: [PATCH 01/15] chore: update usage of datetime.now datetime.datetime.utcnow() is deprecated, replace with tz aware function Note this adds a user warning from numpy but this seems preferable Signed-off-by: Ace Pace --- .../plotly/plotly/tests/test_io/test_to_from_plotly_json.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/python/plotly/plotly/tests/test_io/test_to_from_plotly_json.py b/packages/python/plotly/plotly/tests/test_io/test_to_from_plotly_json.py index 38bbcdaeceb..db325f144ae 100644 --- a/packages/python/plotly/plotly/tests/test_io/test_to_from_plotly_json.py +++ b/packages/python/plotly/plotly/tests/test_io/test_to_from_plotly_json.py @@ -109,7 +109,7 @@ def numpy_unicode_array(request): params=[ datetime.datetime(2003, 7, 12, 8, 34, 22), datetime.datetime.now(), - np.datetime64(datetime.datetime.utcnow()), + np.datetime64(datetime.datetime.now(datetime.timezone.utc)), pd.Timestamp(datetime.datetime.now()), eastern.localize(datetime.datetime(2003, 7, 12, 8, 34, 22)), eastern.localize(datetime.datetime.now()), From 4bb0570fd48c5be2bc98ce895dfb394ae9ee3d68 Mon Sep 17 00:00:00 2001 From: Rob Letzler Date: Tue, 24 Dec 2024 22:59:41 -0500 Subject: [PATCH 02/15] 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 03/15] 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 04/15] 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 05/15] 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 06/15] 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 07/15] 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 08/15] 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() From 9fe747b8f56b1bf716bcb1c96002e4018c2eb1e1 Mon Sep 17 00:00:00 2001 From: Liam Connors Date: Fri, 20 Jun 2025 12:51:36 -0400 Subject: [PATCH 09/15] add kaleido v1 page --- doc/python/getting-started.md | 4 +- doc/python/static-image-export.md | 300 ++++++++++++++++++++---------- 2 files changed, 199 insertions(+), 105 deletions(-) diff --git a/doc/python/getting-started.md b/doc/python/getting-started.md index 9c185f7a43c..acf5c075516 100644 --- a/doc/python/getting-started.md +++ b/doc/python/getting-started.md @@ -41,7 +41,7 @@ The [`plotly` Python library](/python/) is an interactive, [open-source](/python Built on top of the Plotly JavaScript library ([plotly.js](https://plotly.com/javascript/)), `plotly` enables Python users to create beautiful interactive web-based visualizations that can be displayed in Jupyter notebooks, saved to standalone HTML files, or served as part of pure Python-built web applications using Dash. The `plotly` Python library is sometimes referred to as "plotly.py" to differentiate it from the JavaScript library. -Thanks to deep integration with our [Kaleido](https://medium.com/plotly/introducing-kaleido-b03c4b7b1d81) image export utility, `plotly` also provides great support for non-web contexts including desktop editors (e.g. QtConsole, Spyder, PyCharm) and static document publishing (e.g. exporting notebooks to PDF with high-quality vector images). + Thanks to deep integration with our [Kaleido](https://github.com/plotly/Kaleido) image export utility, `plotly` also provides great support for non-web contexts including desktop editors (e.g. QtConsole, Spyder, PyCharm) and static document publishing (e.g. exporting notebooks to PDF with high-quality vector images). This Getting Started guide explains how to install `plotly` and related optional pages. Once you've installed, you can use our documentation in three main ways: @@ -193,7 +193,7 @@ The [`kaleido`](https://github.com/plotly/Kaleido) package has no dependencies a using pip... ``` -$ pip install -U kaleido +$ pip install --upgrade kaleido ``` or conda. diff --git a/doc/python/static-image-export.md b/doc/python/static-image-export.md index 2b31ba0bdd6..96e3d318af0 100644 --- a/doc/python/static-image-export.md +++ b/doc/python/static-image-export.md @@ -5,10 +5,10 @@ jupyter: text_representation: extension: .md format_name: markdown - format_version: '1.2' - jupytext_version: 1.6.0 + format_version: '1.3' + jupytext_version: 1.17.1 kernelspec: - display_name: Python 3 + display_name: Python 3 (ipykernel) language: python name: python3 language_info: @@ -20,7 +20,7 @@ jupyter: name: python nbconvert_exporter: python pygments_lexer: ipython3 - version: 3.7.6 + version: 3.13.3 plotly: description: Plotly allows you to save static images of your plots. Save the image to your local computer, or embed it inside your Jupyter notebooks as a static @@ -35,158 +35,179 @@ jupyter: thumbnail: thumbnail/static-image-export.png --- -### Interactive vs Static Export - -Plotly figures are interactive when viewed in a web browser: you can hover over data points, pan and zoom axes, and show and hide traces by clicking or double-clicking on the legend. You can export figures either to static image file formats like PNG, JPEG, SVG or PDF or you can [export them to HTML files which can be opened in a browser and remain interactive](/python/interactive-html-export/). This page explains how to do the former. - +This page demonstrates how to export interactive Plotly figures to static image formats like PNG, JPEG, SVG, and PDF. If you want to export Plotly figures to HTML to retain interactivity, see the [Interactive HTML Export page](/python/interactive-html-export/) -#### Install Dependencies +## Install Dependencies + +### Kaleido -Static image generation requires either [Kaleido](https://github.com/plotly/Kaleido) (recommended, supported as of `plotly` 4.9) or [orca](https://github.com/plotly/orca) (legacy as of `plotly` 4.9). The `kaleido` package can be installed using pip... +Static image generation requires [Kaleido](https://github.com/plotly/Kaleido). +Install Kaleido with pip: ``` -$ pip install -U kaleido +$ pip install --upgrade kaleido ``` - -or conda. +or with conda: ``` $ conda install -c conda-forge python-kaleido ``` -While Kaleido is now the recommended approach, image export can also be supported by the legacy [orca](https://github.com/plotly/orca) command line utility. See the [Orca Management](/python/orca-management/) section for instructions on installing, configuring, and troubleshooting orca. - - +It's also possible to generate static images using [Orca](https://github.com/plotly/orca), though support for Orca will be removed after September 2025. See the [Orca Management](/python/orca-management/) page for more details. -### Create a Figure +### Chrome -Now let's create a simple scatter plot with 100 random points of varying color and size. +Kaleido uses Chrome for static image generation. Versions of Kaleido prior to v1 included Chrome as part of the Kaleido package. Kaleido v1 does not include Chrome; instead, it looks for a compatible version of Chrome (or Chromium) already installed on the machine on which it's running. -```python -import plotly.graph_objects as go -import numpy as np -np.random.seed(1) - -N = 100 -x = np.random.rand(N) -y = np.random.rand(N) -colors = np.random.rand(N) -sz = np.random.rand(N) * 30 - -fig = go.Figure() -fig.add_trace(go.Scatter( - x=x, - y=y, - mode="markers", - marker=go.scatter.Marker( - size=sz, - color=colors, - opacity=0.6, - colorscale="Viridis" - ) -)) - -fig.show() -``` +If you don't have Chrome installed, you can install it directly from Google following the instructions for your operating system. -### Write Image File +Plotly also provides a CLI for installing Chrome from the command line. -The `plotly.io.write_image` function is used to write an image to a file or file-like python object. You can also use the `.write_image` graph object figure method. +Run `plotly_get_chrome` to install Chrome. -Let's first create an output directory to store our images +You can also install Chrome from within Python using `plotly.io.install_chrome()` ```python -import os +import plotly.io as pio -if not os.path.exists("images"): - os.mkdir("images") +pio.install_chrome() ``` -If you are running this notebook live, click to open the output directory so you can examine the images as they are written. +See the **Additional Information on Browsers with Kaleido** section below for more details on browser compatibility for Kaleido. + + +## Write Image to a File -#### Raster Formats: PNG, JPEG, and WebP +Plotly figures have a `write_image` method to write a figure to a file. `write_image` supports PNG, JPEG, WebP, SVG, and PDF formats. +To export a figure using `write_image`, call `write_image` on the figure, and pass as an argument the filename where you want to save the figure. The file format is inferred from the extension: -plotly.py can output figures to several raster image formats including **PNG**, ... +### Raster Formats + +**PNG** ~~~python -fig.write_image("images/fig1.png") +import plotly.express as px +data_canada = px.data.gapminder().query("country == 'Canada'") +fig = px.bar(data_canada, x='year', y='pop') +fig.write_image("fig1.png") ~~~ -**JPEG**, ... +**JPEG** ~~~python +... fig.write_image("images/fig1.jpeg") ~~~ -and **WebP** +**WebP** ~~~python +... fig.write_image("images/fig1.webp") ~~~ -#### Vector Formats: SVG and PDF... - - -plotly.py can also output figures in several vector formats including **SVG**, ... +### Vector Formats +**SVG** ~~~python +... fig.write_image("images/fig1.svg") ~~~ -**PDF**, ... +**PDF** ~~~python +... fig.write_image("images/fig1.pdf") ~~~ -and **EPS** (requires the poppler library) +--- + +**EPS** (Kaleido<1.0.0) + +Kaleido versions earlier than 1.0.0 also support **EPS** (requires the poppler library). If using Kaleido v1 or later, we recommend PDF or SVG format. ~~~python +... fig.write_image("images/fig1.eps") ~~~ -**Note:** It is important to note that any figures containing WebGL traces (i.e. of type `scattergl`, `contourgl`, `scatter3d`, `surface`, `mesh3d`, `scatterpolargl`, `cone`, `streamtube`, `splom`, or `parcoords`) that are exported in a vector format will include encapsulated rasters, instead of vectors, for some parts of the image. +**Note:** Figures containing WebGL traces (i.e. of type `scattergl`, `contourgl`, `scatter3d`, `surface`, `mesh3d`, `scatterpolargl`, `cone`, `streamtube`, `splom`, or `parcoords`) that are exported in a vector format will include encapsulated rasters, instead of vectors, for some parts of the image. -### Image Export in Dash -[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click "Download" to get the code and run `python app.py`. +### Specify a Format -Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.** +In the earlier example, Plotly inferred the image format from the extension of the filename. You can also specify the format explicitly using the `format` parameter. +~~~python +import plotly.express as px +data_canada = px.data.gapminder().query("country == 'Canada'") +fig = px.bar(data_canada, x='year', y='pop') +fig.write_image("fig1", format="png") +~~~ -```python hide_code=true -from IPython.display import IFrame -snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/' -IFrame(snippet_url + 'static-image-export', width='100%', height=1200) -``` -### Get Image as Bytes + +### Write Multiple Images -The `plotly.io.to_image` function is used to return an image as a bytes object. You can also use the `.to_image` graph object figure method. +*Kaleido v1 and later* -Let convert the figure to a **PNG** bytes object... +`plotly.io` provides a `write_images` function for writing multiple figures to images. Using `write_images` is faster than calling `fig.write_image` multiple times. -```python -img_bytes = fig.to_image(format="png") -``` +`write_images` takes a list of figure objects or dicts representing figures as its first argument, `fig`. The second argument `file` is a list of paths to export to. These paths can be specified as `str`s or [`pathlib.Path` objects](https://docs.python.org/3/library/pathlib.html). + +~~~python +import plotly.graph_objects as go +import plotly.express as px +import plotly.io as pio + + +fig1 = go.Figure( + data=go.Scatter(x=[1, 2, 3], y=[4, 5, 6], mode='lines+markers'), + layout=go.Layout(title='Line Chart') +) + +fig2 = go.Figure( + data=go.Bar(x=['A', 'B', 'C'], y=[10, 5, 15]), + layout=go.Layout(title='Bar Chart') +) + +fig3 = px.pie( + values=[30, 20, 10, 40], + names=['A', 'B', 'C', 'D'], + title='Pie Chart' +) + +pio.write_images( + fig=[fig1, fig2, fig3], + file=['export_images/line_chart.png', 'export_images/bar_chart.png', 'export_images/pie_chart.png'] +) +~~~ + + -and then display the first 20 bytes. +## Get Image as Bytes + +As well as exporting to a file, Plotly figures also support conversion to a bytes object. +To convert a figure to a **PNG** bytes object, call the figure's `to_image` method with a `format` ```python -img_bytes[:20] +import plotly.express as px +data_canada = px.data.gapminder().query("country == 'Canada'") +fig = px.bar(data_canada, x='year', y='pop') +img_bytes = fig.to_image(format="png") ``` -#### Display Bytes as Image Using `IPython.display.Image` -A bytes object representing a PNG image can be displayed directly in the notebook using the `IPython.display.Image` class. This also works in the [Qt Console for Jupyter](https://qtconsole.readthedocs.io/en/stable/)! +Here's the bytes object displayed using `IPython.display.Image`: ```python from IPython.display import Image Image(img_bytes) ``` -### Change Image Dimensions and Scale +## Specify Image Dimensions and Scale In addition to the image format, the `to_image` and `write_image` functions provide arguments to specify the image `width` and `height` in logical pixels. They also provide a `scale` parameter that can be used to increase (`scale` > 1) or decrease (`scale` < 1) the physical resolution of the resulting image. ```python @@ -194,11 +215,11 @@ img_bytes = fig.to_image(format="png", width=600, height=350, scale=2) Image(img_bytes) ``` - -### Specify Image Export Engine +## Specify Image Export Engine + If `kaleido` is installed, it will automatically be used to perform image export. If it is not installed, plotly.py will attempt to use `orca` instead. The `engine` argument to the `to_image` and `write_image` functions can be used to override this default behavior. -Here is an example of specifying that orca should be used: +Here is an example of specifying `orca` for the image export engine: ~~~python fig.to_image(format="png", engine="orca") ~~~ @@ -208,31 +229,104 @@ And, here is an example of specifying that Kaleido should be used: fig.to_image(format="png", engine="kaleido") ~~~ - -### Image Export Settings (Kaleido) -Various image export settings can be configured using the `plotly.io.kaleido.scope` object. For example, the `default_format` property can be used to specify that the default export format should be `svg` instead of `png` +## plotly.io Functions -```python +Previous examples on this page access `write_image` and `to_image` as methods on Plotly Figure objects. This functionality is also available via the `plotly.io` subpackage. + +The following example uses the `write_image` function from `plotly.io`. The function takes the figure or a `dict` representing a figure (as shown in the example) as its first argument. + + +~~~python import plotly.io as pio -pio.kaleido.scope.default_format = "svg" -``` -Here is a complete listing of the available image export settings: - - **`default_width`**: The default pixel width to use on image export. - - **`default_height`**: The default pixel height to use on image export. - - **`default_scale`**: The default image scale factor applied on image export. - - **`default_format`**: The default image format used on export. One of `"png"`, `"jpeg"`, `"webp"`, `"svg"`, `"pdf"`, or `"eps"`. - - **`mathjax`**: Location of the MathJax bundle needed to render LaTeX characters. Defaults to a CDN location. If fully offline export is required, set this to a local MathJax bundle. - - **`topojson`**: Location of the topojson files needed to render choropleth traces. Defaults to a CDN location. If fully offline export is required, set this to a local directory containing the [Plotly.js topojson files](https://github.com/plotly/plotly.js/tree/master/dist/topojson). - - **`mapbox_access_token`**: The default Mapbox access token. +fig = dict({ + "data": [{"type": "bar", + "x": [1, 2, 3], + "y": [1, 3, 2]}], + "layout": {"title": {"text": "A Figure Specified By Python Dictionary"}} +}) +pio.write_image(fig, "fig.png") +~~~ -### Image Export Settings (Orca) -See the [Orca Management](/python/orca-management/) section for information on how to specify image export settings when using orca. +## Image Export Settings (Kaleido) + +As well as configuring height, width, and other settings by passing arguments when calling `write_image` and `to_image`, you can also set a single default to be used throughout the duration of the program. + +### Available Settings + +The following settings are available. + +`default_width`: The default pixel width to use on image export. + +`default_height`: The default pixel height to use on image export. + +`default_scale`: The default image scale factor applied on image export. + +`default_format`: The default image format used on export. One of "png", "jpeg", "webp", "svg", or "pdf". ("eps" support is available with Kaleido v0 only) + +`mathjax`: Location of the MathJax bundle needed to render LaTeX characters. Defaults to a CDN location. If fully offline export is required, set this to a local MathJax bundle. -### Summary -In summary, to export high-quality static images from plotly.py, all you need to do is install the `kaleido` package and then use the `plotly.io.write_image` and `plotly.io.to_image` functions (or the `.write_image` and `.to_image` graph object figure methods). +`topojson`: Location of the topojson files needed to render choropleth traces. Defaults to a CDN location. If fully offline export is required, set this to a local directory containing the Plotly.js topojson files. + +`mapbox_access_token`: The default Mapbox access token (Kaleido v0 only). Mapbox traces are deprecated. See the [MapLibre Migration](https://plotly.com/python/mapbox-to-maplibre/) page for more details. + +### Set Defaults + +Since Plotly.py 6.1, settings are available on `plotly.io.defaults` + +To set the `default_format` to "jpeg": + +~~~python +import plotly.io as pio +pio.defaults.default_format = "jpeg" +~~~ + +You can also access current defaults. To see the default value for height: + +~~~python +import plotly.io as pio +pio.defaults.default_height +~~~ + +In earlier versions of Plotly.py, these settings are available on `plotly.io.kaleido.scope`. + +~~~python +import plotly.io as pio +# Example using `plotly.io.kaleido.scope` +pio.kaleido.scope.default_format = "jpeg" +~~~ + +### Additional Information on Browsers with Kaleido + +When exporting images from Plotly.py, Kaleido will attempt to find a version of [Chrome](https://www.google.com/chrome/index.html) or [Chromium](https://www.chromium.org/getting-involved/download-chromium/) that it can use for the export. It checks in the operating system's PATH for executables with the following names: "chromium", "chromium-browser", "chrome", "Chrome", "google-chrome" "google-chrome-stable", "Chrome.app", "Google Chrome", "Google Chrome.app", and "Google Chrome for Testing". + +Kaleido will also check the following locations: + +**Windows** + +- r"c:\Program Files\Google\Chrome\Application\chrome.exe" +- f"c:\\Users\\{os.environ.get('USER', 'default')}\\AppData\\" +- "Local\\Google\\Chrome\\Application\\chrome.exe" + +**Linux"** + +- "/usr/bin/google-chrome-stable" +- "/usr/bin/google-chrome" +- "/usr/bin/chrome" + +**Mac OS** + +- "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" + +--- + +Most recent versions of Chrome or Chromium should work with Kaleido. Other Chromium-based browsers may also work, though Kaleido won't discover them automatically. You can set a browser to use by setting the path to search using an environment variable called `BROWSER_PATH`. For example: + +``` +BROWSER_PATH=/Applications/Microsoft\ Edge.app/Contents/MacOS/Microsoft\ Edge +``` From 214c35b7b6c6e40f805bc951a724cfc0aebb679b Mon Sep 17 00:00:00 2001 From: Liam Connors Date: Fri, 20 Jun 2025 12:54:16 -0400 Subject: [PATCH 10/15] Update requirements.txt --- doc/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/requirements.txt b/doc/requirements.txt index 65aa3f0aad4..7bf5c01600f 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,4 +1,4 @@ -plotly==6.0.1 +plotly==6.1.2 jupytext==1.16.4 jupyter-client<7 jupyter From ba8bcad0bbaa82cf323d76de50b92b91b4d75cf9 Mon Sep 17 00:00:00 2001 From: Liam Connors Date: Fri, 20 Jun 2025 13:02:33 -0400 Subject: [PATCH 11/15] Update conf.py --- doc/apidoc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/apidoc/conf.py b/doc/apidoc/conf.py index 3c80a1af82f..f3135c16aa3 100644 --- a/doc/apidoc/conf.py +++ b/doc/apidoc/conf.py @@ -26,7 +26,7 @@ # The short X.Y version version = "" # The full version, including alpha/beta/rc tags -release = "6.0.1" +release = "6.1.2" # -- General configuration --------------------------------------------------- From de43923e745cbc535dee73a9445b06cf767a2c85 Mon Sep 17 00:00:00 2001 From: Liam Connors Date: Fri, 20 Jun 2025 13:24:54 -0400 Subject: [PATCH 12/15] Update requirements.txt --- doc/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/requirements.txt b/doc/requirements.txt index 7bf5c01600f..ee6e51f2c0b 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -33,7 +33,7 @@ python-frontmatter datashader==0.14.4 pyarrow cufflinks==0.17.3 -kaleido +kaleido==0.2.1 umap-learn==0.5.1 pooch wget From c1a195850f90000a39547bded5f67d714af6f25c Mon Sep 17 00:00:00 2001 From: Liam Connors Date: Fri, 20 Jun 2025 14:28:56 -0400 Subject: [PATCH 13/15] add back ruff changes --- doc/requirements.txt | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/doc/requirements.txt b/doc/requirements.txt index 5947089e574..c1cb3c5eb62 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,15 +1,9 @@ plotly==6.1.2 -jupytext==1.16.4 -jupyter-client<7 -jupyter -notebook -pandas==1.4.0 -statsmodels==0.14.2 -scipy==1.9.1 -patsy==0.5.6 -numpy==1.22.4 -plotly-geo -igraph +anywidget +cufflinks==0.17.3 +dash-bio +dask==2022.2.0 +datashader==0.14.4 geopandas==0.8.1 geoparse<=2.0.3 igraph @@ -47,15 +41,8 @@ sphinxcontrib-htmlhelp==2.0.0 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.3 sphinxcontrib-serializinghtml==1.1.5 -sphinxcontrib-jsmath==1.0.1 -sphinx_bootstrap_theme -recommonmark -pathlib -python-frontmatter -datashader==0.14.4 -pyarrow -cufflinks==0.17.3 -kaleido +squarify +statsmodels==0.14.2 umap-learn==0.5.1 wget xarray==2022.9.0 From 5cf3121aa384651fb01ea4d5257719a7bd1ebcbc Mon Sep 17 00:00:00 2001 From: Liam Connors Date: Thu, 26 Jun 2025 14:51:11 -0400 Subject: [PATCH 14/15] update to plotly 6.2.0 --- doc/apidoc/conf.py | 2 +- doc/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/apidoc/conf.py b/doc/apidoc/conf.py index 24d3db028c5..02317ef0eef 100644 --- a/doc/apidoc/conf.py +++ b/doc/apidoc/conf.py @@ -24,7 +24,7 @@ # The short X.Y version version = "" # The full version, including alpha/beta/rc tags -release = "6.1.2" +release = "6.2.0" # -- General configuration --------------------------------------------------- diff --git a/doc/requirements.txt b/doc/requirements.txt index c1cb3c5eb62..a874f2f0228 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,4 +1,4 @@ -plotly==6.1.2 +plotly==6.2.0 anywidget cufflinks==0.17.3 dash-bio From 003ef78cf3e12c4d09b8925ec13522252225710f Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Sat, 5 Jul 2025 12:28:57 -0400 Subject: [PATCH 15/15] Fix `height` docstr copypasta --- plotly/basedatatypes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plotly/basedatatypes.py b/plotly/basedatatypes.py index 72f4cd25cac..1384e08d543 100644 --- a/plotly/basedatatypes.py +++ b/plotly/basedatatypes.py @@ -3404,8 +3404,8 @@ def show(self, *args, **kwargs): plot is. The default is set in plotly.js. height: int or float - An integer or float that determines the number of pixels wide the - plot is. The default is set in plotly.js. + An integer or float specifying the height of the plot in pixels. + The default is set in plotly.js. config: dict A dict of parameters to configure the figure. The defaults are set