【问题标题】:How to plot multiple subplots using for loop?如何使用 for 循环绘制多个子图?
【发布时间】:2021-06-14 17:33:59
【问题描述】:

我对 Python 很陌生。我有一个虚拟数据集(25 X 6)用于练习。在 6 列中,我有 1 个目标变量(二进制)和 5 个自变量(4 个分类变量和 1 个数字变量)。我正在尝试通过 4 个分类列中的每一个中的值来查看我的目标分布(并且没有为单独的列编写代码 - 但使用 for 循环使用,以便我可以在将来扩展它以用于更大的数据集)。如下所示:

我已经成功地做到了这一点(上图),但由于我只能通过在 for 循环中使用 计数器 来实现这一点,我认为这不是 Python 优雅的,并且很确定可能有更好的方法(例如CarWash.groupby([i,'ReversedPayment']).size().reset_index().pivot(index = i,columns = 'ReversedPayment',values=0).axes.plot(kind='bar', stacked=True)。我正在努力处理这个ax = 设置)下面是我的非优雅Python代码(不可扩展):

counter = 1
p = 0 
q = 0
fig,axes = plt.subplots(2,2,figsize=(15,10))
for i in categoricals[:-1]:
    CarWash.groupby([i,'ReversedPayment']).size().reset_index().pivot(index = i,columns = 'ReversedPayment',values=0).plot(kind='bar', stacked=True,ax = axes[p][q])
    counter = counter+1
    q = q+1
    if counter==3:
        q=0
        p = p+1

这是完整的数据生成代码:

d = {
    'SeniorCitizen': [0,1,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0] , 
    'CollegeDegree': [0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1] , 
    'Married': [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1] , 
    'FulltimeJob': [1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,0,0,1,1,0,0,0,1] , 
    'DistancefromBranch': [7,9,14,20,21,12,22,25,9,9,9,12,13,14,16,25,27,4,14,14,20,19,15,23,2] , 
    'ReversedPayment': [0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,1,0] }
CarWash = pd.DataFrame(data = d)


categoricals = ['SeniorCitizen','CollegeDegree','Married','FulltimeJob','ReversedPayment']
        numerical = ['DistancefromBranch']
CarWash[categoricals] = CarWash[categoricals].astype('category')

我的另一个小问题是获取数据标签。任何cmets,非常感谢您的建议。谢谢。

【问题讨论】:

标签: python for-loop pandas-groupby


【解决方案1】:

减少代码对许多潜在列重复的最佳方法是创建一个在轴上绘图的函数。这样你基本上可以简单地调整3个参数:

ncols = 2
col_show = 'ReversedPayment'
col_subplots = ['SeniorCitizen','CollegeDegree','Married','FulltimeJob']

现在我们可以从那里计算其余部分。请注意,zip 允许同时直接迭代多个数组,np.flat 像 1D 一样在 2D 轴数组上迭代。

nrows=(len(col_subplots) + ncols - 1) // ncols
fix, axes = plt.subplots(ncols=ncols, nrows=nrows, figsize=(7.5 * ncols, 5 * nrows), sharey=True)
axes_it = axes.flat

for col, ax in zip(col_subplots, axes_it):
    plot_data(CarWash, col_show, col, ax)

# If number of columns not multiple of ncols, hide remaining axes
for ax in axes_it:
    ax.axis('off')

plt.show()

现在在这种情况下,plot_data 非常简单,它几乎不需要是一个函数。但是您可以通过这种方式轻松地对其进行复杂化,并且它允许将数据逻辑与其他基本上是内务管理的部分分开。

  • DataFrame.value_counts() 与 GroupBy.size() 的作用相同,但更明确一些
  • unstack() 将索引级别旋转到列 - 您使用 .reset_index().pivot() 执行此操作。所以现在你有你的列a(这里总是ReversedPayment)作为列,另一列作为索引
  • 最后.plot.bar() 与.plot(kind='bar') 相同,ax 指定要在哪些轴上绘图,rot=0 避免旋转索引并且您已经知道stacked=True。
def plot_data(df, a, b, ax):
    counts = df[[a, b]].value_counts().unstack(a)
    counts.plot.bar(ax=ax, stacked=True, rot=0)

如您所见,subplots(sharey=True) 允许所有图在 y 轴上具有相同的缩放比例,从而更容易比较各种图。

使用迭代器 axes_it 的另一个优点是它会在您停止迭代的地方继续 - 假设您只有 3 个 col_subplots,还剩下 1 个,现在您可以在其上调用 ax.axis('off'):

【讨论】:

  • @Climbali - 感谢您的回答。今天学了点儿新东西。追问:如何在plot() 中标注数据标签? (% 和 #)。 % 会更有用。
  • 这里有很多例子:matplotlib.org/stable/gallery/ticks_and_spines/… 使用 % 你可以做到ax.yaxis.set_major_formatter(matplotlib.ticker.PercentFormatter(xmax=100))
  • @Climbail - 我收到一条错误消息:“DataFrame”对象没有“value_counts”属性。当我运行代码plot_data 部分时。熊猫版本:'0.25.3'
  • 是的,它来自较新的版本。您可以升级或坚持使用GroupBy([a, b]).size()
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-25
  • 1970-01-01
  • 2021-08-02
  • 2022-01-05
  • 2020-09-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多