我一直是 pandas 中的 styler 方法 的粉丝。当我开始构建 Streamlit 应用程序时,对我来说很清楚,我想样式化我的数据框以帮助可视化数据框,但…惊喜!截至写作时,Streamlit st.dataframe() 不支持 Styler 对象,只支持数据框对象。好吧,更正一下,它确实支持它们,但 UI 显示非常糟糕!
这就是为什么我想与你分享我在 Streamlit 中构建优雅数据框的工作方法和想法。我们将涵盖:
-
如何为数字添加千位分隔符。
-
如何将数字显示为百分比 (从数据中的 0.24 到 UI 中的 24%)
-
如何添加货币符号。
-
如何为单元格添加颜色。 更好的是,我将与你分享我最喜欢的颜色分级函数。
-
如何添加表情符号! 是的,我们不能没有表情符号 😊 !
st.dataframe() 的默认视图
Streamlit 实际上非常擅长根据数据类型推断最佳显示。想象以下数据框:
mock_data = {
"Country": ["US", "IN", "BR", "ES", "AR", "IT"],
"Period_1": [50_000, 30_000, 17_000, 14_000, 22_000, 16_000],
"Period_2": [52_000, 37_000, 16_000, 12_000, 21_000, 19_000],
}
df = pd.DataFrame(mock_data)
df['Difference'] = df['Period_2'] - df['Period_1']
df['Percentage Change'] = np.round(((df['Period_2'] - df['Period_1']) / df['Period_1']), 2)
df['Percentage Change rank'] = df['Percentage Change'].rank(method='dense', ascending=False).astype(int)
默认的 st.dataframe() 视图已经很不错了。例如,千位分隔符被正确推断。
图片由作者提供
但是,我们知道我们可以使用 Styler 函数来让它变得更好!
如果我们使用 st.dataframe() 一个 Styler 对象会发生什么?
在深入探讨如何样式化数据框之前,让我们简单地将 pandas 数据框转换为 Styler 对象,并检查当我们尝试使用 st.dataframe() 方法渲染 Styler 对象时会发生什么。
# Creating the styler object
df.copy().style
图片由作者提供
显示现在有点混乱。例如:
-
大数字没有逗号分隔符
-
小数点后 6 位的小数。
这只发生因为我们从“数据框”类型对象转换到了“样式化”类型对象。
格式化千位分隔符和百分比
我们希望一眼就能直观地理解数量级(即千位分隔符)。而且我们不想在心理上乘以小数点 100 来理解百分比(即弄清楚 0.24 是 24%)。
下面的函数将帮助您格式化数字,以便在样式化的数据框中更好地显示。
def _format_with_thousands_commas(val):
return f'{val:,.0f}'
def _format_as_percentage(val, prec=0):
return f'{val:.{prec}%}'
thousands_cols = ['Period_1', 'Period_2', 'Difference']
perct_cols = ['Percentage Change']
styler_with_thousands_commas = (
raw_styler
.format(_format_with_thousands_commas, subset=thousands_cols)
.format(lambda x: _format_as_percentage(x, 2), subset=perct_cols)
)
图片由作者提供
看起来更好了。
-
_ 周期1,_ 周期2 和 差异 列现在与
*st.dataframe()*显示相同。所以,我们至少匹配了默认显示。 -
但现在,百分比变化 列比默认的小数点格式更易于理解 😊。
添加带有渐变的背景着色
带有颜色的 dataframe => Streamlit 中的良好样式化 dataframe(吉米·克里克特说:颜色过多可能有害)。
我们的下一步是添加一些颜色来帮助我们的视觉分析。我们可以使用简单的 cmap 应用单元格背景渐变。默认情况下,cmap 会根据序列中的数字创建渐变(即,取序列的最小值和最大值)。如果你的数字都是正数或负数,这很完美,但以下情况怎么办?
-
仅对正值使用绿色渐变色
-
仅对负值使用红色渐变色。
为了这个,我创建了一个简单的函数,我在多个情况下都重新使用了它:
def _format_positive_negative_background_colour(val, min_value, max_value):
if val > 0:
# Normalize positive values to a scale of 0 to 1
normalized_val = (val - 0) / (max_value - 0)
# Create a gradient of green colors
color = plt.cm.Greens(normalized_val * 0.7)
color_hex = mcolors.to_hex(color)
elif val < 0:
# Normalize negative values to a scale of 0 to -1
normalized_val = (val - min_value) / (0 - min_value)
# Create a gradient of red colors
color = plt.cm.Reds_r(normalized_val * 0.7)
color_hex = mcolors.to_hex(color)
else: color_hex = 'white' # For zero values, set the background color to white
# Determine text color based on the darkness of the background color
r, g, b = mcolors.hex2color(color_hex)
if (r * 299 + g * 587 + b * 114) / 1000 > 0.5: # Use the formula for perceived brightness
text_color = 'black'
else:
text_color = 'white'
return f'background-color: {color_hex}; color: {text_color}'
min_value_abs_diff = df['Difference'].min()
max_value_abs_diff = df['Difference'].max()
min_value_perct_diff = df['Percentage Change'].min()
max_value_perct_diff = df['Percentage Change'].max()
styler_with_colour_gradients = (
df.copy().style
.map(lambda x: _format_positive_negative_background_colour(x, min_value_abs_diff, max_value_abs_diff), subset=['Difference'])
.map(lambda x: _format_positive_negative_background_colour(x, min_value_perct_diff, max_value_perct_diff), subset=['Percentage Change'])
)
作者图片
你可以看到所有正数都着色为绿色,并且从最低(浅色)到最高(深色)有一个阴影。相反,负数着色为红色,但最低的是最深色的颜色。
此外,该函数还确定文本颜色应该是白色还是黑色,这取决于它与单元格背景颜色的对比度。
添加货币符号
如果你知道你正在处理货币,为什么不将货币符号添加到数据中呢?这正是 Excel 所做的,而且嘿,Excel 是一个了不起的产品。老实说,这里没有讽刺。
def _format_with_dollar_sign(val, prec=0):
return f'${val:,.{prec}f}'
styler_with_dollar_sign = (
df.copy().style
.format(_format_with_dollar_sign, subset=['Period_1', 'Period_2', 'Difference'])
)
作者图片
💲 检查 _Period__1, _Period__2 和 _Differenc_e 列 💲
添加表情符号的函数
我们生活在表情符号的世界里,所以你的应用程序在 Streamlit 中没有表情符号的样式化数据框是不可能的。开个玩笑,表情符号也可以帮助我们进行视觉分析。在这个例子中,我使用了金牌、银牌和铜牌表情符号来突出显示最高的正百分比变化。
def _add_medal_emoji(val):
if val == 1:
return f"{val} 🥇"
elif val == 2:
return f"{val} 🥈"
elif val == 3:
return f"{val} 🥉"
else: return val
styler_with_medal_emoji = (
df.copy().style
.format(_add_medal_emoji, subset=['Percentage Change rank'])
)
作者图片
一眼就能看出,哪些国家在 Period 1 和 Period 2 之间有最大的正变化,使用了奖牌表情符号。
目前不支持:添加条形
你是否在 Styler 对象的单元格背景中使用了条形图?遗憾的是,它们在 st.dataframe() 方法中不受支持。您 可以 使用带有 HTML 对象的 st.write() 方法来显示它们,但我 不推荐这样做。下面你可以看到它的样子。
作者图片
不仅用户界面令人震惊……你完全失去了所有 st.dataframe() 功能,例如排序和格式化单元格的能力。
摘要
如果 Streamlit 支持 Styler 对象,生活将会容易得多。但直到那时,我希望这些想法能帮助您在创建 Streamlit 应用程序的优秀可视化过程中。 我将接着使用 AgGrid 构建等效的漂亮数据框。请保持关注,因为 AgGrid 比 Styler 对象酷多了!
代码在哪里可以找到?
在我的仓库和实时 Streamlit 应用程序中:
-
Github 仓库:
github.com/JoseParrenoGarcia/Streamlit-pretty-dataframes -
Streamlit 应用程序:
app-pretty-dataframes-in2ilkxjw2hgby4uqj9bq2.streamlit.app(当链接打开时,点击按钮启动应用程序)。
进一步阅读
感谢阅读这篇文章!如果您对我的其他文章感兴趣,这里有一篇文章收集了我所有其他博客文章,按主题组织:数据科学团队和项目管理、数据故事讲述、营销与出价科学以及机器学习与建模。
请保持关注!
如果您想在我发布新文章时收到通知,请随意在 Medium 上关注我或订阅我的 Substack 通讯。此外,我很乐意在领英上与您聊天!
2371

被折叠的 条评论
为什么被折叠?



