streamlit 1.49.1__py3-none-any.whl → 1.50.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- streamlit/column_config.py +2 -0
- streamlit/commands/navigation.py +3 -1
- streamlit/components/v1/custom_component.py +17 -42
- streamlit/config.py +306 -0
- streamlit/connections/base_connection.py +4 -2
- streamlit/dataframe_util.py +3 -2
- streamlit/delta_generator.py +2 -3
- streamlit/elements/arrow.py +63 -43
- streamlit/elements/deck_gl_json_chart.py +1 -0
- streamlit/elements/form.py +6 -6
- streamlit/elements/graphviz_chart.py +23 -6
- streamlit/elements/iframe.py +0 -2
- streamlit/elements/image.py +10 -9
- streamlit/elements/layouts.py +58 -11
- streamlit/elements/lib/built_in_chart_utils.py +95 -29
- streamlit/elements/lib/column_config_utils.py +5 -0
- streamlit/elements/lib/column_types.py +563 -144
- streamlit/elements/lib/dialog.py +1 -0
- streamlit/elements/lib/pandas_styler_utils.py +30 -14
- streamlit/elements/lib/utils.py +17 -5
- streamlit/elements/map.py +1 -3
- streamlit/elements/media.py +2 -0
- streamlit/elements/metric.py +10 -32
- streamlit/elements/plotly_chart.py +17 -9
- streamlit/elements/pyplot.py +6 -6
- streamlit/elements/vega_charts.py +110 -44
- streamlit/elements/widgets/audio_input.py +48 -0
- streamlit/elements/widgets/button.py +27 -25
- streamlit/elements/widgets/button_group.py +1 -0
- streamlit/elements/widgets/camera_input.py +1 -0
- streamlit/elements/widgets/chat.py +1 -0
- streamlit/elements/widgets/checkbox.py +1 -0
- streamlit/elements/widgets/color_picker.py +1 -0
- streamlit/elements/widgets/data_editor.py +6 -5
- streamlit/elements/widgets/file_uploader.py +1 -0
- streamlit/elements/widgets/multiselect.py +10 -0
- streamlit/elements/widgets/number_input.py +3 -0
- streamlit/elements/widgets/radio.py +1 -0
- streamlit/elements/widgets/select_slider.py +1 -0
- streamlit/elements/widgets/selectbox.py +4 -0
- streamlit/elements/widgets/slider.py +1 -0
- streamlit/elements/widgets/text_widgets.py +6 -0
- streamlit/elements/widgets/time_widgets.py +9 -0
- streamlit/elements/write.py +1 -17
- streamlit/git_util.py +65 -43
- streamlit/material_icon_names.py +1 -1
- streamlit/proto/Arrow_pb2.py +10 -8
- streamlit/proto/Arrow_pb2.pyi +31 -2
- streamlit/proto/AudioInput_pb2.py +2 -2
- streamlit/proto/AudioInput_pb2.pyi +6 -2
- streamlit/proto/Block_pb2.py +11 -11
- streamlit/proto/Block_pb2.pyi +5 -0
- streamlit/proto/NewSession_pb2.py +18 -16
- streamlit/proto/NewSession_pb2.pyi +135 -2
- streamlit/runtime/app_session.py +18 -5
- streamlit/runtime/theme_util.py +148 -0
- streamlit/static/index.html +2 -2
- streamlit/static/manifest.json +222 -222
- streamlit/static/static/css/index.CHEnSPGk.css +1 -0
- streamlit/static/static/css/{index.C8X8rNzw.css → index.CIiu7Ygf.css} +1 -1
- streamlit/static/static/js/{ErrorOutline.esm.DcGrhbBP.js → ErrorOutline.esm.DUpR0_Ka.js} +1 -1
- streamlit/static/static/js/{FileDownload.esm.DgBvV6Pq.js → FileDownload.esm.CN4j9-1w.js} +1 -1
- streamlit/static/static/js/{FileHelper.M6AAaeuA.js → FileHelper.CaIUKG91.js} +1 -1
- streamlit/static/static/js/{FormClearHelper.DHh1GFzm.js → FormClearHelper.DTcdrasw.js} +1 -1
- streamlit/static/static/js/{Hooks.DGu1od_L.js → Hooks.BRba_Own.js} +1 -1
- streamlit/static/static/js/InputInstructions.xnSDuYeQ.js +1 -0
- streamlit/static/static/js/{Particles.DDVT-6Qc.js → Particles.CElH0XX2.js} +1 -1
- streamlit/static/static/js/{ProgressBar.BEY0cXXV.js → ProgressBar.DetlP5aY.js} +2 -2
- streamlit/static/static/js/Toolbar.C77ar7rq.js +1 -0
- streamlit/static/static/js/{base-input.CK3UVGp1.js → base-input.BQft14La.js} +3 -3
- streamlit/static/static/js/{checkbox.D8W881TL.js → checkbox.yZOfXCeX.js} +1 -1
- streamlit/static/static/js/{createSuper.B6W-Dh9S.js → createSuper.Dh9w1cs8.js} +1 -1
- streamlit/static/static/js/data-grid-overlay-editor.DcuHuCyW.js +1 -0
- streamlit/static/static/js/{downloader.DiKpuU_S.js → downloader.MeHtkq8r.js} +1 -1
- streamlit/static/static/js/{es6.B8zRNPZ-.js → es6.VpBPGCnM.js} +2 -2
- streamlit/static/static/js/{iframeResizer.contentWindow.DIewJmmh.js → iframeResizer.contentWindow.yMw_ARIL.js} +1 -1
- streamlit/static/static/js/{index.B9mjBcgE.js → index.64ejlaaT.js} +1 -1
- streamlit/static/static/js/{index.CD8HuT3N.js → index.6xX1278W.js} +90 -91
- streamlit/static/static/js/index.B-hiXRzw.js +1 -0
- streamlit/static/static/js/{index.Ch7MBCx0.js → index.B0H9IXUJ.js} +47 -47
- streamlit/static/static/js/{index.4eF4NxG2.js → index.B4cAbHP6.js} +1 -1
- streamlit/static/static/js/{index.Dk4C7X3i.js → index.B4dUQfni.js} +1 -1
- streamlit/static/static/js/{index.CvYYtxD_.js → index.BPQo7BKk.js} +1 -1
- streamlit/static/static/js/index.Baqa90pe.js +2 -0
- streamlit/static/static/js/{index.D5naqx-J.js → index.Bj9JgOEC.js} +1 -1
- streamlit/static/static/js/index.BjCwMzj4.js +3 -0
- streamlit/static/static/js/{index.C_tmcx4B.js → index.Bm3VbPB5.js} +1 -1
- streamlit/static/static/js/{index.C7fRKRs4.js → index.Bxz2yX3P.js} +1 -1
- streamlit/static/static/js/{index.ho6NIXGl.js → index.BycLveZ4.js} +1 -1
- streamlit/static/static/js/{index.452cqrrL.js → index.C9BdUqTi.js} +1 -1
- streamlit/static/static/js/index.CFMf5_ez.js +197 -0
- streamlit/static/static/js/index.CGYqqs6j.js +1 -0
- streamlit/static/static/js/{index.zecpGxtj.js → index.CH1tqnSs.js} +1 -1
- streamlit/static/static/js/{index.CjXWwH-y.js → index.CMItVsFA.js} +1 -1
- streamlit/static/static/js/{index.B6U8LQo3.js → index.CTBk8Vk2.js} +1 -1
- streamlit/static/static/js/index.CiAQIz1H.js +7 -0
- streamlit/static/static/js/index.Cj7DSzVR.js +73 -0
- streamlit/static/static/js/index.Ck8rQ9OL.js +1 -0
- streamlit/static/static/js/{index.Ts_0SdB9.js → index.ClELlchS.js} +2 -2
- streamlit/static/static/js/{index.Bte_9Lyq.js → index.Cnpi3o3E.js} +1 -1
- streamlit/static/static/js/{index.CcJf6BCU.js → index.Ctn27_AE.js} +1 -1
- streamlit/static/static/js/{index.CP5TD2z1.js → index.D2QEXQq_.js} +1 -1
- streamlit/static/static/js/index.DH71Ezyj.js +1 -0
- streamlit/static/static/js/{index.D2-atlaQ.js → index.DHh-U0dK.js} +2 -2
- streamlit/static/static/js/{index.DtYN2x4k.js → index.DK7hD7_w.js} +1 -1
- streamlit/static/static/js/{index.qhs54UAB.js → index.DKv_lNO7.js} +1 -1
- streamlit/static/static/js/index.DNLrMXgm.js +12 -0
- streamlit/static/static/js/index.DW0Grddz.js +1 -0
- streamlit/static/static/js/{index.cnnXF7xQ.js → index.Dbe-Q3C-.js} +1 -1
- streamlit/static/static/js/index.DcPNYEUo.js +1 -0
- streamlit/static/static/js/index.DuxqVQpd.js +1 -0
- streamlit/static/static/js/{index.CejBxbg1.js → index.FFOzOWzC.js} +1 -1
- streamlit/static/static/js/{index.BnEpvLEz.js → index.GRUzrudl.js} +1 -1
- streamlit/static/static/js/{input.nzVJphXi.js → input.s6pjQ49A.js} +1 -1
- streamlit/static/static/js/{memory.CjCgTQz3.js → memory.Cuvsdfrl.js} +1 -1
- streamlit/static/static/js/{number-overlay-editor.DaRFzZEO.js → number-overlay-editor.DdgVR5m3.js} +1 -1
- streamlit/static/static/js/{possibleConstructorReturn.DgiPnZ9N.js → possibleConstructorReturn.CqidKeei.js} +1 -1
- streamlit/static/static/js/{sandbox.mithfq7Z.js → sandbox.CCQREcJx.js} +1 -1
- streamlit/static/static/js/{timepicker.Dbl5KFh6.js → timepicker.mkJF97Bb.js} +4 -4
- streamlit/static/static/js/{toConsumableArray.D-Dx88BQ.js → toConsumableArray.De7I7KVR.js} +1 -1
- streamlit/static/static/js/{uniqueId.Bh26R_3S.js → uniqueId.RI1LJdtz.js} +1 -1
- streamlit/static/static/js/{useBasicWidgetState.DeK-QJpD.js → useBasicWidgetState.CedkNjUW.js} +1 -1
- streamlit/static/static/js/{useTextInputAutoExpand.4iAdLWD-.js → useTextInputAutoExpand.Ca7w8dVs.js} +2 -2
- streamlit/static/static/js/{useUpdateUiValue.CmT7_nJN.js → useUpdateUiValue.DeXelfRH.js} +1 -1
- streamlit/static/static/js/withFullScreenWrapper.C3561XxJ.js +1 -0
- streamlit/static/static/media/MaterialSymbols-Rounded.DeCZgS-4.woff2 +0 -0
- streamlit/string_util.py +58 -1
- streamlit/web/bootstrap.py +0 -31
- streamlit/web/server/routes.py +17 -4
- streamlit/web/server/server.py +1 -0
- {streamlit-1.49.1.dist-info → streamlit-1.50.0.dist-info}/METADATA +1 -1
- {streamlit-1.49.1.dist-info → streamlit-1.50.0.dist-info}/RECORD +136 -135
- streamlit/static/static/css/index.COe1010n.css +0 -1
- streamlit/static/static/js/InputInstructions.z6sVgyYt.js +0 -1
- streamlit/static/static/js/Toolbar.DSnK1fUh.js +0 -1
- streamlit/static/static/js/data-grid-overlay-editor.DRTHOydk.js +0 -1
- streamlit/static/static/js/index.BXYmrqnf.js +0 -1
- streamlit/static/static/js/index.B_8AnktO.js +0 -1
- streamlit/static/static/js/index.Bl7zGQSh.js +0 -7
- streamlit/static/static/js/index.BnJIOYn9.js +0 -73
- streamlit/static/static/js/index.C1HcTl5K.js +0 -1
- streamlit/static/static/js/index.C7lSmSOP.js +0 -1
- streamlit/static/static/js/index.D3K5nOu9.js +0 -197
- streamlit/static/static/js/index.DkKT3LUI.js +0 -1
- streamlit/static/static/js/index.MTPPBDHk.js +0 -2
- streamlit/static/static/js/index.pqW9AMJD.js +0 -3
- streamlit/static/static/js/index.urHgTgMQ.js +0 -12
- streamlit/static/static/js/index.wzkv_11M.js +0 -1
- streamlit/static/static/js/index.yF5AncHY.js +0 -1
- streamlit/static/static/js/withFullScreenWrapper.DLp1ENGm.js +0 -1
- streamlit/static/static/media/MaterialSymbols-Rounded.CBxVaFdk.woff2 +0 -0
- {streamlit-1.49.1.data → streamlit-1.50.0.data}/scripts/streamlit.cmd +0 -0
- {streamlit-1.49.1.dist-info → streamlit-1.50.0.dist-info}/WHEEL +0 -0
- {streamlit-1.49.1.dist-info → streamlit-1.50.0.dist-info}/entry_points.txt +0 -0
- {streamlit-1.49.1.dist-info → streamlit-1.50.0.dist-info}/top_level.txt +0 -0
|
@@ -19,14 +19,7 @@ from __future__ import annotations
|
|
|
19
19
|
from dataclasses import dataclass
|
|
20
20
|
from datetime import date
|
|
21
21
|
from enum import Enum
|
|
22
|
-
from typing import
|
|
23
|
-
TYPE_CHECKING,
|
|
24
|
-
Any,
|
|
25
|
-
Final,
|
|
26
|
-
Literal,
|
|
27
|
-
TypedDict,
|
|
28
|
-
cast,
|
|
29
|
-
)
|
|
22
|
+
from typing import TYPE_CHECKING, Any, Final, Literal, TypedDict, cast
|
|
30
23
|
|
|
31
24
|
from typing_extensions import TypeAlias
|
|
32
25
|
|
|
@@ -47,6 +40,10 @@ if TYPE_CHECKING:
|
|
|
47
40
|
import pandas as pd
|
|
48
41
|
|
|
49
42
|
from streamlit.dataframe_util import Data
|
|
43
|
+
from streamlit.elements.lib.layout_utils import (
|
|
44
|
+
Height,
|
|
45
|
+
Width,
|
|
46
|
+
)
|
|
50
47
|
|
|
51
48
|
VegaLiteType: TypeAlias = Literal["quantitative", "ordinal", "temporal", "nominal"]
|
|
52
49
|
ChartStackType: TypeAlias = Literal["normalize", "center", "layered"]
|
|
@@ -59,6 +56,7 @@ class PrepDataColumns(TypedDict):
|
|
|
59
56
|
y_column_list: list[str]
|
|
60
57
|
color_column: str | None
|
|
61
58
|
size_column: str | None
|
|
59
|
+
sort_column: str | None
|
|
62
60
|
|
|
63
61
|
|
|
64
62
|
@dataclass
|
|
@@ -73,13 +71,14 @@ class AddRowsMetadata:
|
|
|
73
71
|
columns: PrepDataColumns
|
|
74
72
|
# Chart styling properties
|
|
75
73
|
color: str | Color | list[Color] | None = None
|
|
76
|
-
width:
|
|
77
|
-
height:
|
|
78
|
-
use_container_width: bool =
|
|
74
|
+
width: Width | None = None
|
|
75
|
+
height: Height | None = None
|
|
76
|
+
use_container_width: bool | None = None
|
|
79
77
|
# Only applicable for bar & area charts
|
|
80
78
|
stack: bool | ChartStackType | None = None
|
|
81
79
|
# Only applicable for bar charts
|
|
82
80
|
horizontal: bool = False
|
|
81
|
+
sort: bool | str = False
|
|
83
82
|
|
|
84
83
|
|
|
85
84
|
class ChartType(Enum):
|
|
@@ -151,13 +150,14 @@ def generate_chart(
|
|
|
151
150
|
y_axis_label: str | None = None,
|
|
152
151
|
color_from_user: str | Color | list[Color] | None = None,
|
|
153
152
|
size_from_user: str | float | None = None,
|
|
154
|
-
width:
|
|
155
|
-
height:
|
|
156
|
-
use_container_width: bool =
|
|
153
|
+
width: Width | None = None,
|
|
154
|
+
height: Height | None = None,
|
|
155
|
+
use_container_width: bool | None = None,
|
|
157
156
|
# Bar & Area charts only:
|
|
158
157
|
stack: bool | ChartStackType | None = None,
|
|
159
158
|
# Bar charts only:
|
|
160
159
|
horizontal: bool = False,
|
|
160
|
+
sort_from_user: bool | str = False,
|
|
161
161
|
) -> tuple[alt.Chart | alt.LayerChart, AddRowsMetadata]:
|
|
162
162
|
"""Function to use the chart's type, data columns and indices to figure out the
|
|
163
163
|
chart's spec.
|
|
@@ -181,6 +181,8 @@ def generate_chart(
|
|
|
181
181
|
# Get name of column to use for size, or constant value to use. Any/both could
|
|
182
182
|
# be None.
|
|
183
183
|
size_column, size_value = _parse_generic_column(df, size_from_user)
|
|
184
|
+
# Get name of column to use for sort.
|
|
185
|
+
sort_column = _parse_sort_column(df, sort_from_user)
|
|
184
186
|
|
|
185
187
|
# Store some info so we can use it in add_rows.
|
|
186
188
|
add_rows_metadata = AddRowsMetadata(
|
|
@@ -194,6 +196,7 @@ def generate_chart(
|
|
|
194
196
|
"y_column_list": y_column_list,
|
|
195
197
|
"color_column": color_column,
|
|
196
198
|
"size_column": size_column,
|
|
199
|
+
"sort_column": sort_column,
|
|
197
200
|
},
|
|
198
201
|
# Chart styling properties
|
|
199
202
|
color=color_from_user,
|
|
@@ -202,13 +205,13 @@ def generate_chart(
|
|
|
202
205
|
use_container_width=use_container_width,
|
|
203
206
|
stack=stack,
|
|
204
207
|
horizontal=horizontal,
|
|
208
|
+
sort=sort_from_user,
|
|
205
209
|
)
|
|
206
210
|
|
|
207
211
|
# At this point, all foo_column variables are either None/empty or contain actual
|
|
208
212
|
# columns that are guaranteed to exist.
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
df, x_column, y_column_list, color_column, size_column
|
|
213
|
+
df, x_column, y_column, color_column, size_column, sort_column = _prep_data(
|
|
214
|
+
df, x_column, y_column_list, color_column, size_column, sort_column
|
|
212
215
|
)
|
|
213
216
|
|
|
214
217
|
# At this point, x_column is only None if user did not provide one AND df is empty.
|
|
@@ -224,14 +227,18 @@ def generate_chart(
|
|
|
224
227
|
x_axis_label,
|
|
225
228
|
y_axis_label,
|
|
226
229
|
stack,
|
|
230
|
+
sort_from_user,
|
|
227
231
|
)
|
|
228
232
|
|
|
233
|
+
chart_width = width if isinstance(width, int) else None
|
|
234
|
+
chart_height = height if isinstance(height, int) else None
|
|
235
|
+
|
|
229
236
|
# Create a Chart with x and y encodings.
|
|
230
237
|
chart = alt.Chart(
|
|
231
238
|
data=df,
|
|
232
239
|
mark=chart_type.value["mark_type"],
|
|
233
|
-
width=
|
|
234
|
-
height=
|
|
240
|
+
width=chart_width or 0,
|
|
241
|
+
height=chart_height or 0,
|
|
235
242
|
).encode(
|
|
236
243
|
x=x_encoding,
|
|
237
244
|
y=y_encoding,
|
|
@@ -281,7 +288,7 @@ def generate_chart(
|
|
|
281
288
|
and is_altair_version_5_or_greater
|
|
282
289
|
):
|
|
283
290
|
return _add_improved_hover_tooltips(
|
|
284
|
-
chart, x_column,
|
|
291
|
+
chart, x_column, chart_width, chart_height
|
|
285
292
|
).interactive(), add_rows_metadata
|
|
286
293
|
|
|
287
294
|
return chart.interactive(), add_rows_metadata
|
|
@@ -359,6 +366,7 @@ def prep_chart_data_for_add_rows(
|
|
|
359
366
|
y_column_list=add_rows_metadata.columns["y_column_list"],
|
|
360
367
|
color_column=add_rows_metadata.columns["color_column"],
|
|
361
368
|
size_column=add_rows_metadata.columns["size_column"],
|
|
369
|
+
sort_column=add_rows_metadata.columns["sort_column"],
|
|
362
370
|
)
|
|
363
371
|
|
|
364
372
|
return out_data, add_rows_metadata
|
|
@@ -437,7 +445,8 @@ def _prep_data(
|
|
|
437
445
|
y_column_list: list[str],
|
|
438
446
|
color_column: str | None,
|
|
439
447
|
size_column: str | None,
|
|
440
|
-
|
|
448
|
+
sort_column: str | None = None,
|
|
449
|
+
) -> tuple[pd.DataFrame, str | None, str | None, str | None, str | None, str | None]:
|
|
441
450
|
"""Prepares the data for charting. This is also used in add_rows.
|
|
442
451
|
|
|
443
452
|
Returns the prepared dataframe and the new names of the x column (taking the index
|
|
@@ -450,7 +459,7 @@ def _prep_data(
|
|
|
450
459
|
|
|
451
460
|
# Drop columns we're not using.
|
|
452
461
|
selected_data = _drop_unused_columns(
|
|
453
|
-
df, x_column, color_column, size_column, *y_column_list
|
|
462
|
+
df, x_column, color_column, size_column, sort_column, *y_column_list
|
|
454
463
|
)
|
|
455
464
|
|
|
456
465
|
# Maybe convert color to Vega colors.
|
|
@@ -462,17 +471,18 @@ def _prep_data(
|
|
|
462
471
|
y_column_list,
|
|
463
472
|
color_column,
|
|
464
473
|
size_column,
|
|
474
|
+
sort_column,
|
|
465
475
|
) = _convert_col_names_to_str_in_place(
|
|
466
|
-
selected_data, x_column, y_column_list, color_column, size_column
|
|
476
|
+
selected_data, x_column, y_column_list, color_column, size_column, sort_column
|
|
467
477
|
)
|
|
468
478
|
|
|
469
479
|
# Maybe melt data from wide format into long format.
|
|
470
480
|
melted_data, y_column, color_column = _maybe_melt(
|
|
471
|
-
selected_data, x_column, y_column_list, color_column, size_column
|
|
481
|
+
selected_data, x_column, y_column_list, color_column, size_column, sort_column
|
|
472
482
|
)
|
|
473
483
|
|
|
474
484
|
# Return the data, but also the new names to use for x, y, and color.
|
|
475
|
-
return melted_data, x_column, y_column, color_column, size_column
|
|
485
|
+
return melted_data, x_column, y_column, color_column, size_column, sort_column
|
|
476
486
|
|
|
477
487
|
|
|
478
488
|
def _last_index_for_melted_dataframes(
|
|
@@ -505,7 +515,7 @@ def _is_date_column(df: pd.DataFrame, name: str | None) -> bool:
|
|
|
505
515
|
if column.size == 0:
|
|
506
516
|
return False
|
|
507
517
|
|
|
508
|
-
return isinstance(column.
|
|
518
|
+
return isinstance(column.iat[0], date)
|
|
509
519
|
|
|
510
520
|
|
|
511
521
|
def _melt_data(
|
|
@@ -636,7 +646,7 @@ def _maybe_convert_color_column_in_place(
|
|
|
636
646
|
if color_column is None or len(df[color_column]) == 0:
|
|
637
647
|
return
|
|
638
648
|
|
|
639
|
-
first_color_datum = df[color_column].
|
|
649
|
+
first_color_datum = df[color_column].iat[0]
|
|
640
650
|
|
|
641
651
|
if is_hex_color_like(first_color_datum):
|
|
642
652
|
# Hex is already CSS-valid.
|
|
@@ -657,7 +667,8 @@ def _convert_col_names_to_str_in_place(
|
|
|
657
667
|
y_column_list: list[str],
|
|
658
668
|
color_column: str | None,
|
|
659
669
|
size_column: str | None,
|
|
660
|
-
|
|
670
|
+
sort_column: str | None,
|
|
671
|
+
) -> tuple[str | None, list[str], str | None, str | None, str | None]:
|
|
661
672
|
"""Converts column names to strings, since Vega-Lite does not accept ints, etc."""
|
|
662
673
|
import pandas as pd
|
|
663
674
|
|
|
@@ -670,6 +681,7 @@ def _convert_col_names_to_str_in_place(
|
|
|
670
681
|
[str(c) for c in y_column_list],
|
|
671
682
|
None if color_column is None else str(color_column),
|
|
672
683
|
None if size_column is None else str(size_column),
|
|
684
|
+
None if sort_column is None else str(sort_column),
|
|
673
685
|
)
|
|
674
686
|
|
|
675
687
|
|
|
@@ -703,6 +715,17 @@ def _parse_x_column(df: pd.DataFrame, x_from_user: str | None) -> str | None:
|
|
|
703
715
|
)
|
|
704
716
|
|
|
705
717
|
|
|
718
|
+
def _parse_sort_column(df: pd.DataFrame, sort_from_user: bool | str) -> str | None:
|
|
719
|
+
if sort_from_user is False or sort_from_user is True:
|
|
720
|
+
return None
|
|
721
|
+
|
|
722
|
+
sort_column = sort_from_user.removeprefix("-")
|
|
723
|
+
if sort_column not in df.columns:
|
|
724
|
+
raise StreamlitColumnNotFoundError(df, sort_column)
|
|
725
|
+
|
|
726
|
+
return sort_column
|
|
727
|
+
|
|
728
|
+
|
|
706
729
|
def _parse_y_columns(
|
|
707
730
|
df: pd.DataFrame,
|
|
708
731
|
y_from_user: str | Sequence[str] | None,
|
|
@@ -790,6 +813,7 @@ def _maybe_melt(
|
|
|
790
813
|
y_column_list: list[str],
|
|
791
814
|
color_column: str | None,
|
|
792
815
|
size_column: str | None,
|
|
816
|
+
sort_column: str | None,
|
|
793
817
|
) -> tuple[pd.DataFrame, str | None, str | None]:
|
|
794
818
|
"""If multiple columns are set for y, melt the dataframe into long format."""
|
|
795
819
|
y_column: str | None
|
|
@@ -806,6 +830,8 @@ def _maybe_melt(
|
|
|
806
830
|
columns_to_leave_alone = [x_column]
|
|
807
831
|
if size_column:
|
|
808
832
|
columns_to_leave_alone.append(size_column)
|
|
833
|
+
if sort_column:
|
|
834
|
+
columns_to_leave_alone.append(sort_column)
|
|
809
835
|
|
|
810
836
|
df = _melt_data(
|
|
811
837
|
df=df,
|
|
@@ -828,8 +854,10 @@ def _get_axis_encodings(
|
|
|
828
854
|
x_axis_label: str | None,
|
|
829
855
|
y_axis_label: str | None,
|
|
830
856
|
stack: bool | ChartStackType | None,
|
|
857
|
+
sort_from_user: bool | str,
|
|
831
858
|
) -> tuple[alt.X, alt.Y]:
|
|
832
859
|
stack_encoding: alt.X | alt.Y
|
|
860
|
+
sort_encoding: alt.X | alt.Y
|
|
833
861
|
if chart_type == ChartType.HORIZONTAL_BAR:
|
|
834
862
|
# Handle horizontal bar chart - switches x and y data:
|
|
835
863
|
x_encoding = _get_x_encoding(
|
|
@@ -839,6 +867,7 @@ def _get_axis_encodings(
|
|
|
839
867
|
df, x_column, x_from_user, y_axis_label, chart_type
|
|
840
868
|
)
|
|
841
869
|
stack_encoding = x_encoding
|
|
870
|
+
sort_encoding = y_encoding
|
|
842
871
|
else:
|
|
843
872
|
x_encoding = _get_x_encoding(
|
|
844
873
|
df, x_column, x_from_user, x_axis_label, chart_type
|
|
@@ -847,10 +876,15 @@ def _get_axis_encodings(
|
|
|
847
876
|
df, y_column, y_from_user, y_axis_label, chart_type
|
|
848
877
|
)
|
|
849
878
|
stack_encoding = y_encoding
|
|
879
|
+
sort_encoding = x_encoding
|
|
850
880
|
|
|
851
881
|
# Handle stacking - only relevant for bar & area charts
|
|
852
882
|
_update_encoding_with_stack(stack, stack_encoding)
|
|
853
883
|
|
|
884
|
+
# Handle sorting - only relevant for bar charts
|
|
885
|
+
if chart_type in (ChartType.VERTICAL_BAR, ChartType.HORIZONTAL_BAR):
|
|
886
|
+
_update_encoding_with_sort(sort_from_user, sort_encoding)
|
|
887
|
+
|
|
854
888
|
return x_encoding, y_encoding
|
|
855
889
|
|
|
856
890
|
|
|
@@ -957,6 +991,38 @@ def _update_encoding_with_stack(
|
|
|
957
991
|
encoding["stack"] = stack
|
|
958
992
|
|
|
959
993
|
|
|
994
|
+
def _update_encoding_with_sort(
|
|
995
|
+
sort_from_user: bool | str,
|
|
996
|
+
encoding: alt.X | alt.Y,
|
|
997
|
+
) -> None:
|
|
998
|
+
"""Apply sort to the given encoding in-place.
|
|
999
|
+
|
|
1000
|
+
- If sort is False: disable Altair's default sorting on the bar's categorical axis
|
|
1001
|
+
(i.e., set to None).
|
|
1002
|
+
- If sort is True: use Altair's default sorting.
|
|
1003
|
+
- If sort is a column name (optionally starting with '-') set a SortField with the correct order.
|
|
1004
|
+
|
|
1005
|
+
Note: Column validation should be done before calling this function.
|
|
1006
|
+
"""
|
|
1007
|
+
import altair as alt
|
|
1008
|
+
|
|
1009
|
+
if sort_from_user is False:
|
|
1010
|
+
# Disable Altair's default sorting
|
|
1011
|
+
encoding["sort"] = None
|
|
1012
|
+
elif sort_from_user is True:
|
|
1013
|
+
# Use Altair's default sorting
|
|
1014
|
+
pass
|
|
1015
|
+
else:
|
|
1016
|
+
# String: sort by column name (optional '-' prefix for descending)
|
|
1017
|
+
sort_order: Literal["ascending", "descending"]
|
|
1018
|
+
if sort_from_user.startswith("-"):
|
|
1019
|
+
sort_order = "descending"
|
|
1020
|
+
else:
|
|
1021
|
+
sort_order = "ascending"
|
|
1022
|
+
sort_field = sort_from_user.removeprefix("-")
|
|
1023
|
+
encoding["sort"] = alt.SortField(field=sort_field, order=sort_order)
|
|
1024
|
+
|
|
1025
|
+
|
|
960
1026
|
def _get_color_encoding(
|
|
961
1027
|
df: pd.DataFrame,
|
|
962
1028
|
color_value: Color | None,
|
|
@@ -1022,7 +1088,7 @@ def _get_color_encoding(
|
|
|
1022
1088
|
|
|
1023
1089
|
# If the 0th element in the color column looks like a color, we'll use the color
|
|
1024
1090
|
# column's values as the colors in our chart.
|
|
1025
|
-
elif len(df[color_column]) and is_color_like(df[color_column].
|
|
1091
|
+
elif len(df[color_column]) and is_color_like(df[color_column].iat[0]):
|
|
1026
1092
|
color_range = [to_css_color(c) for c in df[color_column].unique()]
|
|
1027
1093
|
color_enc["scale"] = alt.Scale(range=color_range)
|
|
1028
1094
|
# Don't show the color legend, because it will just show text with the
|
|
@@ -109,6 +109,11 @@ _EDITING_COMPATIBILITY_MAPPING: Final[dict[ColumnType, list[ColumnDataKind]]] =
|
|
|
109
109
|
ColumnDataKind.STRING,
|
|
110
110
|
ColumnDataKind.EMPTY,
|
|
111
111
|
],
|
|
112
|
+
"multiselect": [
|
|
113
|
+
ColumnDataKind.LIST,
|
|
114
|
+
ColumnDataKind.STRING,
|
|
115
|
+
ColumnDataKind.EMPTY,
|
|
116
|
+
],
|
|
112
117
|
}
|
|
113
118
|
|
|
114
119
|
|