Skip to content

Commit

Permalink
Prefer column config formatting over Pandas styler (#9538)
Browse files Browse the repository at this point in the history
## Describe your changes

We apply Pandas styler display values for text, number, uri, and
datetime cells. Technically, we cannot detect which cells in the
dataframe actually got styled by the user, and which didn't. Therefore,
we currently just apply all available display values. However, this also
overwrites the formatting that a user might have specified via column
config (e.g. `format` for number & datetime column or `display_text` for
link column). This PR changes the behaviour to only apply the styled
value if formatting wasn't configured via column config.

## GitHub Issue Link (if applicable)

- Closes #7329
- Closes #7977

## Testing Plan

- Added e2e tests.
- Added unit tests.

---

**Contribution License Agreement**

By submitting this pull request you agree that all contributions to this
project are made under the Apache 2.0 license.

---------

Co-authored-by: Lukas Masuch <103002573+sfc-gh-lmasuch@users.noreply.github.com>
  • Loading branch information
lukasmasuch and sfc-gh-lmasuch authored Sep 25, 2024
1 parent 38a19eb commit 5f0501d
Show file tree
Hide file tree
Showing 11 changed files with 133 additions and 25 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 40 additions & 0 deletions e2e_playwright/st_dataframe_styler_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,43 @@ def make_pretty(styler):
).style.format(lambda url: url.replace("https://", "Website: ") if url else ""),
column_config={"col_0": st.column_config.LinkColumn()},
)

st.header("Column config takes prio over Pandas Styler")
st.dataframe(
pd.DataFrame(
{
"number (formatted)": [1, 2, 3],
"url (formatted)": [
"https://streamlit.io",
"https://docs.streamlit.io",
"https://docs.streamlit.io",
],
"datetime (formatted)": [
pd.Timestamp("2024-01-01"),
pd.Timestamp("2024-01-02"),
pd.Timestamp("2024-01-03"),
],
"text": ["foo", "bar", "foobar"],
"number": [1, 2, 3],
"url": [
"https://streamlit.io",
"https://docs.streamlit.io",
"https://docs.streamlit.io",
],
"datetime": [
pd.Timestamp("2024-01-01"),
pd.Timestamp("2024-01-02"),
pd.Timestamp("2024-01-03"),
],
}
).style.format(lambda v: "pd styler"),
column_config={
"number (formatted)": st.column_config.NumberColumn(format="$%.2f"),
"url (formatted)": st.column_config.LinkColumn(display_text="Open"),
"datetime (formatted)": st.column_config.DatetimeColumn(format="MMM DD, YYYY"),
"number": st.column_config.NumberColumn(),
"url": st.column_config.LinkColumn(),
"datetime": st.column_config.DatetimeColumn(),
},
hide_index=True,
)
3 changes: 2 additions & 1 deletion e2e_playwright/st_dataframe_styler_support_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
def test_dataframe_pd_styler(themed_app: Page, assert_snapshot: ImageCompareFunction):
"""Test that st.dataframe supports styling and display values via Pandas Styler."""
elements = themed_app.get_by_test_id("stDataFrame")
expect(elements).to_have_count(5)
expect(elements).to_have_count(6)

# The dataframe component might require a bit more time for rendering the canvas
themed_app.wait_for_timeout(250)
Expand All @@ -30,3 +30,4 @@ def test_dataframe_pd_styler(themed_app: Page, assert_snapshot: ImageCompareFunc
assert_snapshot(elements.nth(2), name="st_dataframe-styler_background_and_font")
assert_snapshot(elements.nth(3), name="st_dataframe-styler_gradient")
assert_snapshot(elements.nth(4), name="st_dataframe-styler_link_display_value")
assert_snapshot(elements.nth(5), name="st_dataframe-column_config_over_styler")
45 changes: 45 additions & 0 deletions frontend/lib/src/components/widgets/DataFrame/arrowUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,51 @@ describe("getCellFromArrow", () => {
expect((cell as any).data.displayDate).toEqual("FOOO")
})

it("doesnt apply display content from styler if format is set", () => {
const MOCK_TIME_COLUMN = {
...TimeColumn({
id: "1",
name: "time_column",
title: "Time column",
indexNumber: 0,
isEditable: false,
isHidden: false,
isIndex: false,
isStretched: false,
columnTypeOptions: {
format: "YYYY",
},
arrowType: {
pandas_type: "time",
numpy_type: "object",
},
}),
}

// Create a mock arrowCell object with time data
const arrowCell = {
// Unix timestamp in microseconds Wed Sep 29 2021 21:13:20
// Our default unit is seconds, so it needs to be adjusted internally
content: BigInt(1632950000123000),
contentType: null,
field: {
type: {
unit: 2, // Microseconds
},
},
displayContent: "FOOO",
cssId: null,
cssClass: null,
type: "columns",
} as object as DataFrameCell

// Call the getCellFromArrow function
const cell = getCellFromArrow(MOCK_TIME_COLUMN, arrowCell)
// Should use the formatted value from the cell and not the displayContent
// from pandas styler
expect((cell as any).data.displayDate).toEqual("2021")
})

it("parses numeric timestamps for time columns into valid Date values", () => {
const MOCK_TIME_COLUMN = {
...TimeColumn({
Expand Down
25 changes: 22 additions & 3 deletions frontend/lib/src/components/widgets/DataFrame/arrowUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,12 @@ import {
ColumnCreator,
DateColumn,
DateTimeColumn,
DateTimeColumnParams,
isErrorCell,
LinkColumnParams,
ListColumn,
NumberColumn,
NumberColumnParams,
ObjectColumn,
removeLineBreaks,
SelectboxColumn,
Expand Down Expand Up @@ -453,19 +456,35 @@ export function getCellFromArrow(
...cellTemplate,
displayData,
} as TextCell
} else if (cellTemplate.kind === GridCellKind.Number) {
} else if (
cellTemplate.kind === GridCellKind.Number &&
// Only apply styler value if format was not explicitly set by the user.
isNullOrUndefined(
(column.columnTypeOptions as NumberColumnParams)?.format
)
) {
cellTemplate = {
...cellTemplate,
displayData,
} as NumberCell
} else if (cellTemplate.kind === GridCellKind.Uri) {
} else if (
cellTemplate.kind === GridCellKind.Uri &&
// Only apply styler value if display text was not explicitly set by the user.
isNullOrUndefined(
(column.columnTypeOptions as LinkColumnParams)?.display_text
)
) {
cellTemplate = {
...cellTemplate,
displayData,
} as UriCell
} else if (
cellTemplate.kind === GridCellKind.Custom &&
(cellTemplate as DatePickerType).data?.kind === "date-picker-cell"
(cellTemplate as DatePickerType).data?.kind === "date-picker-cell" &&
// Only apply styler value if format was not explicitly set by the user.
isNullOrUndefined(
(column.columnTypeOptions as DateTimeColumnParams)?.format
)
) {
cellTemplate = {
...cellTemplate,
Expand Down
45 changes: 24 additions & 21 deletions frontend/lib/src/components/widgets/DataFrame/columns/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,27 @@
* limitations under the License.
*/

import ObjectColumn from "./ObjectColumn"
import TextColumn from "./TextColumn"
import CheckboxColumn from "./CheckboxColumn"
import SelectboxColumn from "./SelectboxColumn"
import ListColumn from "./ListColumn"
import NumberColumn from "./NumberColumn"
import LinkColumn from "./LinkColumn"
import ImageColumn from "./ImageColumn"
import ProgressColumn from "./ProgressColumn"
import DateTimeColumn, { DateColumn, TimeColumn } from "./DateTimeColumn"
import {
AreaChartColumn,
BarChartColumn,
LineChartColumn,
} from "./ChartColumn"
import CheckboxColumn from "./CheckboxColumn"
import DateTimeColumn, { DateColumn, TimeColumn } from "./DateTimeColumn"
import ImageColumn from "./ImageColumn"
import LinkColumn from "./LinkColumn"
import ListColumn from "./ListColumn"
import NumberColumn from "./NumberColumn"
import ObjectColumn from "./ObjectColumn"
import ProgressColumn from "./ProgressColumn"
import SelectboxColumn from "./SelectboxColumn"
import TextColumn from "./TextColumn"
import { ColumnCreator } from "./utils"

export { ImageCellEditor } from "./cells/ImageCellEditor"
export type { DateTimeColumnParams } from "./DateTimeColumn"
export type { LinkColumnParams } from "./LinkColumn"
export type { NumberColumnParams } from "./NumberColumn"

export * from "./utils"

Expand Down Expand Up @@ -63,19 +66,19 @@ export const ColumnTypes = new Map<string, ColumnCreator>(
export const CustomCells = []

export {
ObjectColumn,
TextColumn,
AreaChartColumn,
BarChartColumn,
CheckboxColumn,
SelectboxColumn,
ListColumn,
NumberColumn,
LinkColumn,
DateTimeColumn,
DateColumn,
TimeColumn,
LineChartColumn,
BarChartColumn,
AreaChartColumn,
DateTimeColumn,
ImageColumn,
LineChartColumn,
LinkColumn,
ListColumn,
NumberColumn,
ObjectColumn,
ProgressColumn,
SelectboxColumn,
TextColumn,
TimeColumn,
}

0 comments on commit 5f0501d

Please sign in to comment.