Altair保存数据到磁盘时图例顺序异常,如何维持正确排序?
问题描述
现有如下DataFrame:
A B C 0 1.624345 -0.611756 2 1 -1.072969 0.865408 1 2 1.744812 -0.761207 2 3 -0.249370 1.462108 2 4 -0.322417 -0.384054 11 5 -1.099891 -0.172428 1 6 0.042214 0.582815 1 7 1.144724 0.901591 11 8 0.900856 -0.683728 11 9 -0.935769 -0.267888 2
使用A、B列绘制散点图并按C列着色,执行以下代码时:
alt.Chart(randf).mark_point(size=100, filled=True).encode( x="A", y="B", color="C:N" ).properties(width=200, height=200)
图例顺序为1、2、11,符合数值排序预期。
但当使用自定义CSV数据转换器将数据保存到磁盘后(代码如下):
import os from toolz.curried import pipe def csv_dir(data, data_dir="altairdata") -> None: os.makedirs(data_dir, exist_ok=True) return pipe(data, alt.to_csv(filename=data_dir + "/{prefix} -{hash}.{extension}")) alt.data_transformers.register("csv_dir", csv_dir) alt.data_transformers.enable("csv_dir", data_dir="./.temporary_altair_data/")
再次绘制相同图表,图例顺序变成1、11、2,是按字符串排序而非数值排序。
需要解决的问题:在数据保存到磁盘的情况下,如何让图例保持正确的数值排序?
完整复现代码:
import pandas as pd import altair as alt import numpy as np import os from toolz.curried import pipe np.random.seed(1) randf = pd.DataFrame(np.random.randn(10, 3), columns=list("ABC")) randf["C"] = np.random.choice([1, 2, 11], 10) # 内嵌数据时的图表 chart1 = alt.Chart(randf).mark_point(size=100, filled=True).encode( x="A", y="B", color="C:N" ) chart1 # 启用磁盘存储数据后的图表 def csv_dir(data, data_dir="altairdata") -> None: os.makedirs(data_dir, exist_ok=True) return pipe(data, alt.to_csv(filename=data_dir + "/{prefix} -{hash}.{extension}")) alt.data_transformers.register("csv_dir", csv_dir) alt.data_transformers.enable("csv_dir", data_dir="./.temporary_altair_data/") chart2 = alt.Chart(randf).mark_point(size=100, filled=True).encode( x="A", y="B", color="C:N" ) chart2
解决方案
问题根源是:数据保存为CSV文件时,C列数值会被默认解析为字符串类型,Altair对字符串字段按字典序排序,导致11排在2前面。可通过以下方式解决:
方法1:明确指定字段类型
在编码时将C:N改为有序分类类型C:O或定量类型C:Q,让Altair按数值排序:
chart2 = alt.Chart(randf).mark_point(size=100, filled=True).encode( x="A", y="B", color=alt.Color("C:O", title="C") ).properties(width=200, height=200)
C:O(有序分类)保留分类特性,同时按数值排序;C:Q(定量)会将C视为连续数值,图例变为颜色条,按需选择。
方法2:使用类型保留的存储格式
改用Parquet格式存储数据(Parquet会完整保留数据类型),修改数据转换器:
def parquet_dir(data, data_dir="altairdata") -> None: os.makedirs(data_dir, exist_ok=True) return pipe(data, alt.to_parquet(filename=data_dir + "/{prefix} -{hash}.{extension}")) alt.data_transformers.register("parquet_dir", parquet_dir) alt.data_transformers.enable("parquet_dir", data_dir="./.temporary_altair_data/")
Parquet适合大数据场景,避免CSV的类型丢失问题。
方法3:手动指定图例排序
通过sort参数强制指定排序顺序:
# 获取C列唯一值并按数值排序 sorted_values = sorted(randf["C"].unique()) chart2 = alt.Chart(randf).mark_point(size=100, filled=True).encode( x="A", y="B", color=alt.Color("C:N", sort=sorted_values, title="C") ).properties(width=200, height=200)
该方法灵活性高,适合自定义排序需求。
内容的提问来源于stack exchange,提问作者Thomas
相关产品推荐
相关产品推荐

