如何将 seaborn 用于带有嵌套数据的时间序列箱线图?

How to use seaborn for a time series boxplot with nested data?

情况

我正在尝试创建包含个人数据和 nested/grouped 数据的箱线图。我使用的数据集表示许多家庭的信息,其中 1 相和 3 相系统之间存在区别 (#)

#NOTE Where the id appears only once, the household is single phased (1-phase) and duplicates are 3-phase system. Due to the duplicates, reading the csv-file via pd.read_csv(..) will extend the duplicate's names (i.e. 1, 1.1 and 1.2).

使用基本的绘图技巧可以实现:

In [4]: VoltageProfileFile= pd.read_csv(dest + '/VoltageProfiles_' + str(PV_par['value_PV']) + '%PV.csv', dtype= 'float')
   ...: VoltageProfileFile.boxplot(figsize=(20,5), rot= 60)
   ...: plt.ylim(0.9, 1.1)
   ...: plt.show()

Out[4]:

结果是正确的,但如果只有 1 个刻度代表 1、1.1 和 1.2 或 5、5.1、5.2 等,那就干净了。

问题

我想通过使用“分类”箱线图来清理它,其中来自重复项(三相系统)的值被分组在相同的 ID 下。我知道 seaborn 允许用户使用 hue 参数:sns.boxplot(x='',hue='', y='', data='') 来创建分类图 (Plotting with categorical data). However, I can’t figure out how to format my dataset in order to achieve this? I tried via pd.melt(..) function (cfr. pandas.melt),但生成的格式会更改值出现的顺序 (*)

(*) Every id is accompanied by a length up to a reference point, thus the order of appearance on the x-axis must remain.

解决这个问题的好方法是什么? 理想情况下,箱线图会将 3 相系统分组在一个 id 下,并为 1ph 和 3ph 系统显示不同的颜色。

亲切的问候,

雷米

对于 seaborn 绘图,数据的结构应该是长格式而不是宽格式,因为它具有不同的指标,例如 householdphase, .

因此考虑实际让 Pandas 重命名第 1、1.1、1.2 列,然后 运行 pd.melt 调整生成的 household 和 [=15] 为长格式=] 使用 assign 的列,您在 . 上拆分并分别获取第一部分和第二部分:

VoltageProfileFile_long = (pd.melt(VoltageProfileFile, var_name = 'phase')
                             .assign(household = lambda x: x['phase'].str.split("\.").str[0].astype(int),
                                     phase = lambda x: pd.to_numeric(x['phase'].str.split("\.").str[1]).fillna(0).astype(int).add(1))
                             .reindex(['household', 'phase', 'value'], axis='columns')
                          )

下面是一个随机数据的演示

数据 (转储到 csv 然后读回 pandas 重命名过程)

np.random.seed(111620)

VoltageProfileFile = pd.DataFrame([np.random.uniform(0.95, 1.05, 13) for i in range(50)], 
                                  columns = [1, 1, 1, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9])
VoltageProfileFile.to_csv('data.csv', index=False)

VoltageProfileFile = pd.read_csv('data.csv')
VoltageProfileFile.head(10)
#           1       1.1       1.2         2         3  ...       5.2         6         7         8         9
# 0  1.012732  1.042768  0.975577  0.965508  1.048544  ...  1.010898  1.008921  1.006769  1.019615  1.036926
# 1  1.013457  1.048378  1.025201  0.982988  0.995133  ...  1.024578  1.024362  0.985693  1.041609  0.995037
# 2  1.024739  1.008590  0.960278  0.956811  1.001739  ...  0.969436  0.953134  0.966851  1.031544  1.036572
# 3  1.037998  0.993246  0.970146  0.989196  0.959527  ...  1.015577  1.027020  1.038941  0.971666  1.040658
# 4  0.995877  0.955734  0.952497  1.040942  0.985759  ...  1.021805  1.044108  0.980657  1.034179  0.980722
# 5  0.994755  0.951557  0.986580  1.021583  0.959249  ...  1.046740  0.998429  1.027406  1.007391  0.989477
# 6  1.023979  1.043418  1.020745  1.006081  1.030413  ...  0.964579  1.035479  0.982969  0.953484  1.005889
# 7  1.018904  1.045440  1.003997  1.018295  0.954814  ...  0.955295  0.960958  0.999492  1.010163  0.985847
# 8  0.960913  0.982671  1.016659  1.030384  1.043750  ...  1.042720  0.972287  1.039235  0.969571  0.999418
# 9  1.017085  0.998049  0.989664  0.953420  1.018018  ...  0.953041  0.955883  1.004630  0.996443  1.017762

Plot (经过同样的处理生成VoltageProfileFile_long

sns.set()

fig, ax = plt.subplots(figsize=(8,4))

sns.boxplot(x='household', y='value', hue='phase', data=VoltageProfileFile_long, ax=ax)
plt.title('Boxplot of Values by Household and Phases')
plt.tight_layout()

plt.show()
plt.clf()
plt.close()