在 pandas 多索引数据帧上绘制两个级别的 x_ticklabels [重复]

Posted

技术标签:

【中文标题】在 pandas 多索引数据帧上绘制两个级别的 x_ticklabels [重复]【英文标题】:Plot two levels of x_ticklabels on a pandas multi-index dataframe [duplicate] 【发布时间】:2019-01-19 23:18:20 【问题描述】:

我有一个多索引数据框,其中索引是从日期导出的。它包含年份和季度值。

我想要实现的是在 x 轴上有两组刻度标签的图。次要刻度标签应代表季度值(1 到 4),主要刻度标签应代表年份值。但是,我不希望显示所有年份的刻度标签,只显示四个季度中每个季度的唯一年份。

这很容易在 Excel 图表中表示,这是我试图重现的示例。

这是我数据集中的一个示例。

serotype_df = pd.DataFrame('13v': (2002, 1): 5,
  (2002, 2): 9,
  (2002, 3): 23,
  (2002, 4): 11,
  (2003, 1): 1,
  (2003, 2): 12,
  (2003, 3): 22,
  (2003, 4): 15,
  (2004, 1): 10,
  (2004, 2): 11,
  (2004, 3): 30,
  (2004, 4): 11,
  (2005, 1): 9,
  (2005, 2): 20,
  (2005, 3): 20,
  (2005, 4): 7,
 '23v': (2002, 1): 1,
  (2002, 2): 8,
  (2002, 3): 18,
  (2002, 4): 5,
  (2003, 1): 5,
  (2003, 2): 16,
  (2003, 3): 13,
  (2003, 4): 7,
  (2004, 1): 4,
  (2004, 2): 4,
  (2004, 3): 20,
  (2004, 4): 5,
  (2005, 1): 4,
  (2005, 2): 5,
  (2005, 3): 10,
  (2005, 4): 5,
 '7v': (2002, 1): 30,
  (2002, 2): 75,
  (2002, 3): 148,
  (2002, 4): 68,
  (2003, 1): 26,
  (2003, 2): 75,
  (2003, 3): 147,
  (2003, 4): 67,
  (2004, 1): 32,
  (2004, 2): 84,
  (2004, 3): 151,
  (2004, 4): 62,
  (2005, 1): 21,
  (2005, 2): 49,
  (2005, 3): 81,
  (2005, 4): 26,
 'Non-typed': (2002, 1): 1,
  (2002, 2): 2,
  (2002, 3): 4,
  (2002, 4): 4,
  (2003, 1): 3,
  (2003, 2): 5,
  (2003, 3): 9,
  (2003, 4): 8,
  (2004, 1): 1,
  (2004, 2): 4,
  (2004, 3): 6,
  (2004, 4): 4,
  (2005, 1): 4,
  (2005, 2): 10,
  (2005, 3): 7,
  (2005, 4): 11,
 'Non-vaccine': (2002, 1): 2,
  (2002, 2): 7,
  (2002, 3): 10,
  (2002, 4): 6,
  (2003, 1): 4,
  (2003, 2): 5,
  (2003, 3): 13,
  (2003, 4): 8,
  (2004, 1): 2,
  (2004, 2): 4,
  (2004, 3): 19,
  (2004, 4): 8,
  (2005, 1): 4,
  (2005, 2): 3,
  (2005, 3): 15,
  (2005, 4): 5)

我尝试使用来自不同 SO 示例的一些代码。这是我试过的代码。

import pandas as pd
import matplotlib.pyplot as plt

fig = plt.figure(figsize=(14,8), dpi=200) 
ax = fig.add_subplot(111)
ax1 = ax.twiny()

serotype_df.plot(kind='bar', ax=ax, stacked='True');


trunc = lambda x: x.strip("()").split(" ")[1]
tl = [ trunc(t.get_text()) for t in ax.get_xticklabels()]
ax.set_xticklabels(tl,rotation=0);


serotype_df.plot(kind='bar', ax=ax1, stacked='True');

trunc0 = lambda x: x.strip("()").split(", ")[0]
tl = [ trunc0(t.get_text()) for t in ax1.get_xticklabels()]
ax1.set_xticklabels(tl);

我正好有我想要的四分之一 xlabels。我似乎无法获得独特的年份值。

非常感谢任何帮助。

【问题讨论】:

你也可以看看***.com/a/39502106/1504082,它很好地解决了这个问题。 【参考方案1】:

试试下面的代码。这是通过为您的情况下的每个level[0] index 创建一个子图year 并将其用作x_label 来实现的。对于每个子图,我们绘制数据。

def plot_function(x, ax):
    ax = graph[x]
    ax.set_xlabel(x, weight='bold')
    return serotype_df.xs(x).plot(kind='bar', stacked='True', ax=ax, legend=False)

n_subplots = len(serotype_df.index.levels[0])
fig, axes = plt.subplots(nrows=1, ncols=n_subplots, sharey=True, figsize=(14, 8))  # width, height

graph = dict(zip(serotype_df.index.levels[0], axes))
plots = list(map(lambda x: plot_function(x, graph[x]), graph))
ax.tick_params(axis='both', which='both', length=0)
fig.subplots_adjust(wspace=0)

plt.legend()
plt.show()

如果您没有对每个子图进行太多更改,您始终可以执行以下操作:

plots = list(map(lambda x: serotype_df.xs(x).plot(kind='bar', stacked='True', ax=graph[x], legend=False).set_xlabel(x, weight='bold'), graph))

这样您就不必创建或使用plot_function

【讨论】:

我觉得这个解决方案很棒,但我相信通过使用 for 循环而不是函数可以使其更具可读性。我在this answer 中分享了一个示例,该示例也适用于子图之间列数不同的情况(例如,如果当前年份仅包括 2 个季度)。

以上是关于在 pandas 多索引数据帧上绘制两个级别的 x_ticklabels [重复]的主要内容,如果未能解决你的问题,请参考以下文章

Pandas - Groupby 多索引级别,获取可能的组合,然后转换数据

Pandas 从多索引级别获取所有值

绘制 pandas 多索引 DataFrame,其中一个索引作为 Y 轴,另一个作为 X 轴

pandas:选择索引,然后选择多索引切片上的列

在多索引列上合并pandas数据帧

Pandas:将多索引级别作为系列