【问题标题】:How to make stacked line chart with different y-axis in matplotlib?如何在matplotlib中制作不同y轴的堆叠折线图?
【发布时间】:2021-07-29 22:50:44
【问题描述】:

我想知道如何制作堆叠折线图,它将在 matplotlib 中采用不同的列。关键是当我们进行聚合时,我需要在两个不同的列上进行数据聚合,我想我需要制作一个用于绘图的大数据框。我没有在 pandas、matplotlib 中找到更漂亮、更方便的方法。谁能建议可能的调整来做到这一点?有什么想法吗?

我的尝试

这是我需要做的第一个聚合:

import pandas as pd
import matplotlib.pyplot as plt

url = "https://gist.githubusercontent.com/adamFlyn/4657714653398e9269263a7c8ad4bb8a/raw/fa6709a0c41888503509e569ace63606d2e5c2ff/mydf.csv"
df = pd.read_csv(url, parse_dates=['date'])

df_re = df[df['retail_item'].str.contains("GROUND BEEF")]
df_rei = df_re.groupby(['date', 'retail_item']).agg({'number_of_ads': 'sum'})
df_rei = df_rei.reset_index(level=[0,1])
df_rei['week'] = pd.DatetimeIndex(df_rei['date']).week
df_rei['year'] = pd.DatetimeIndex(df_rei['date']).year
df_rei['week'] = df_rei['date'].dt.strftime('%W').astype('uint8')

df_ret_df1 = df_rei.groupby(['retail_item', 'week'])['number_of_ads'].agg([max, min, 'mean']).stack().reset_index(level=[2]).rename(columns={'level_2': 'mm', 0: 'vals'}).reset_index()

这是我需要做的第二次聚合,类似于第一次聚合,除了我现在选择不同的列:

df_re['price_gap'] = df_re['high_price'] - df_re['low_price']
dff_rei1 = df_re.groupby(['date', 'retail_item']).agg({'price_gap': 'mean'})
dff_rei1 = dff_rei1.reset_index(level=[0,1])
dff_rei1['week'] = pd.DatetimeIndex(dff_rei1['date']).week
dff_rei1['year'] = pd.DatetimeIndex(dff_rei1['date']).year
dff_rei1['week'] = dff_rei1['date'].dt.strftime('%W').astype('uint8')

dff_ret_df2 = dff_rei1.groupby(['retail_item', 'week'])['price_gap'].agg([max, min, 'mean']).stack().reset_index(level=[2]).rename(columns={'level_2': 'mm', 0: 'vals'}).reset_index()

现在我正在苦苦挣扎,如何将第一个、第二个聚合的输出组合到一个数据框中以制作堆叠折线图。有可能这样做吗?

目标

我想制作堆叠折线图,其中 y 轴采用不同的列,例如 y 轴应该显示广告数量和价格范围,而 x 轴显示 52 周。这是我尝试制作折线图的部分代码:

for g, d in df_ret_df1.groupby('retail_item'):
    fig, ax = plt.subplots(figsize=(7, 4), dpi=144)
    sns.lineplot(x='week', y='vals', hue='mm', data=d,alpha=.8)
    y1 = d[d.mm == 'max']
    y2 = d[d.mm == 'min']
    plt.fill_between(x=y1.week, y1=y1.vals, y2=y2.vals)
    
    for year in df['year'].unique():
        data = df_rei[(df_rei.date.dt.year == year) & (df_rei.retail_item == g)]
        sns.lineplot(x='week', y='price_gap', ci=None, data=data,label=year,alpha=.8)

有什么优雅的方法可以让我们构建绘图数据,在 pandas 中可以轻松完成不同列上的数据聚合?还有其他方法可以实现这一点吗?有什么想法吗?

想要的输出

这是我想要获得的所需输出:

我应该如何绘制数据才能得到我想要的这样的图?有什么想法吗?

【问题讨论】:

  • 缺乏答案的一个原因可能是我们必须对您尝试通过两个聚合实现的目标进行逆向工程。我建议添加对这些步骤的作用以及最终图表应该显示的内容的描述。我也有错误信息,例如NameError: name 'cmap' is not defined
  • @Mr.T 我附上了我最终想要的情节并修复了错误。我提出了这个问题,你对这个问题有什么更好的想法吗?谢谢!
  • 我现在没有时间研究这个问题。也许本周晚些时候,但我认为到那时其他人会解决你的问题。
  • 对我来说,这个问题看起来像是 your previous question 的副本。最好澄清你以前的问题,而不是发布一个新问题。我将看到有关编辑 my answer 以根据您现在提供的所需输出提供解决方案的信息。

标签: python pandas matplotlib seaborn


【解决方案1】:

Pandas 的 groupby 功能非常通用,您可以大大减少代码行数以实现最终的数据帧进行绘图。

plotdf = df_re.groupby([ 'retail_item',df_re['date'].dt.year,df_re['date'].dt.week]).agg({'number_of_ads':'sum','price_gap':'mean'}).unstack().T

以正确的方式完成聚合后,使用 for 循环在不同的绘图中显示所需的每个度量。通过使用 pandas describe 功能来动态计算最小值和最大值来绘制阴影范围:

f,axs = plt.subplots(2,1,figsize=(20,14))
axs=axs.ravel()

for i,x in enumerate(['number_of_ads','price_gap']):
    plotdf.loc[x].plot(rot=90,grid=True,ax=axs[i])
    plotdf.loc[x].T.describe().T[['min','max']].plot(kind='area',color=['w','grey'],alpha=0.3,ax=axs[i],title= x)

使用更新的代码进行编辑:

plotdf = df_re.groupby(['retail_item',df_re['date'].dt.year,df_re['date'].dt.week]).agg({'number_of_ads':'sum','weighted_avg':'mean'}).unstack().T
f,axs = plt.subplots(3,2,figsize=(20,14))
axs=axs.ravel()
i=0
for col in plotdf.columns.get_level_values(0).unique():
    for x in ['number_of_ads','weighted_avg']:
        plotdf.loc[x,col].plot(rot=90,grid=True,ax=axs[i]);
      plotdf.loc[x,col].T.describe().T[['min','max']].plot(kind='area',color=['w','grey'],alpha=0.3,ax=axs[i],title= col+', '+x)
        i+=1

【讨论】:

  • 为什么在第 17 周到第 45 周之间没有数据显示?我知道情节的结构看起来是对的,但情节本身是不对的。您可以进行任何可能的更新吗?我们可以在 y 轴上使用 weighted_avg,而不是使用 price_range,在这种情况下,可能的输出将如何?任何更新?谢谢!
  • 源数据已更改 - 它不再包含 2019 年的数据。如果你更新它,它会显示出来。至于 weighted_avg,是的,可以得到任何你想要的度量,你只需要在第一行写一个聚合函数而不是 'mean':'price_gap':'mean'
  • 当我对 weighted_avg 进行聚合时,出现错误。如果我们想在 weighted_avg 上进行聚合,您介意添加代码行吗?另外,我们是否可以按年份分开子图,例如 2020 年、2021 年的每个零售项目在一个堆叠图中?我们是否可以使用 for 循环对零售商品进行迭代,其中每个堆叠图显示 2020 年、2021 年广告数量和价格范围的特定商品?我们怎么能像我在想要的情节中展示的那样做到这一点?有什么想法吗?谢谢
  • 我能够使用 weighted_avg 而不是 price_gap 运行它。请检查您的代码。上面的编辑代码生成 6 个图表,每个产品 2 个。如果您的所有原始问题都得到解决,请标记为答案。
  • 我们能否在 y 轴上汇总最高价、最低价和加权平均价格,同时保持与您在更新尝试中显示的完全相同的格式?那可行吗?你介意展示我们应该如何处理这个问题吗?我肯定会接受答案,但这个问题悬在我身边。再次感谢您的帮助!
猜你喜欢
  • 2023-02-04
  • 1970-01-01
  • 2011-01-14
  • 2019-12-07
  • 2013-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-12
相关资源
最近更新 更多