【问题标题】:How to get pandas to plot on the same graph with the same y axis range如何让熊猫在具有相同 y 轴范围的同一张图上绘图
【发布时间】:2017-11-23 22:29:25
【问题描述】:

我正在尝试将多个条形图垂直叠加。应该有一个标记为 x 轴(带有星期几)。我到目前为止的代码是:

import pandas as pd
import matplotlib.pyplot as plt
import calendar

df = pd.read_csv("health.csv", header = None, names = ['Physical', 'Emotional'])
# Get Dayofweek index number (start with 6 for sunday) 6,0,1....
df['DayOfTheWeek'] = [(i+6) % 7  for i in range(len(df))]

# Get a map to translate to day of week
d = dict(zip(range(7),list(calendar.day_name)))
df['DayOfTheWeek'] = df['DayOfTheWeek'].map(d)

# Loop through the df (splitting week by week)
for i in range(int(round(len(df)/7))):
    plt.ylim([0,10])
    df.iloc[i*7:(i+1)*7].set_index('DayOfTheWeek').plot(kind='bar')
plt.show()

这有以下问题:

  1. 由于某些原因,生成的第一张图是空白的。
  2. 我希望同一图表上的子图垂直分开,而不是许多单独的图
  3. 我的数据框有 39 行,但上面的方法根本没有绘制最后 4 个点。

完整的输入数据是:

5,5
6,7
6,9
6,7
5,6
7,9
5,9
6,7
7,6
7,4
7,5
6,7
7,9
7,9
5,6
8,7
9,9
7,7
7,6
7,8
7,9
7,9
7,6
7,8
6,6
6,6
6,7
6,6
6,5
6,6
7,5
7,5
7,5
7,6
7,5
8,6
7,6
7,7
6,6

【问题讨论】:

    标签: python pandas matplotlib


    【解决方案1】:

    1。由于某些原因,生成的第一张图是空白的。

    当您调用plt.ylim() 时,它将“设置当前轴的 y 限制”。它通过调用 plt.gca under the hood 来实现这一点,这将“获取当前的 Axes 实例 (...),或者创建一个。”。现在,在循环的第一次迭代中,不存在 Axes,因此它创建了一个新的。然后 pandas.DataFrame.plot 继续创建自己的图形,忽略现有图形。这就是你如何得到一个空的第一个情节。

    解决方法很简单:交换plt.ylim([0,10])和下面一行的顺序,或者直接设置在.plot(kind='bar', ylim=(0, 10))中。

    2。我希望在同一图表上垂直分开的子图,而不是许多单独的图

    也许plt.subplots() 就是您要找的东西?

    n_weeks = 6  # See pt 3 for an elaboration on this
    fig, axs = plt.subplots(n_weeks, 1, figsize=(5, 12), sharex=True)
    
    # Record the names of the first 7 days in the dataset
    weekdays = df.head(7)['DayOfTheWeek'].values
    for weekno, ax in enumerate(axs):
        week = df.iloc[weekno*7:(weekno+1)*7]
        week = week.set_index('DayOfTheWeek')
        # The final week is incomplete and will mess up our plot unless
        # we force it to contain all the weekdays.
        week = week.loc[weekdays]
        week.plot(kind='bar', ylim=(0, 10), ax=ax, legend=False)
    # Only draw legend in the final Axis
    ax.legend()
    
    # Force tight layout
    fig.tight_layout()
    

    3。我的数据框有 39 行,但上面的方法根本没有绘制最后 4 个点。

    尝试打印您在循环中选择的范围,您应该能够发现错误。这是一个off-by-one error :-)

    以下剧透/解决方案!

    for i in range(int(round(len(df)/7))):
        print(df.iloc[i*7:(i+1)*7])
    

    表明您只选择完整的周数。

    注意:在复制问题中的数据时,我显然漏掉了一行!应该有 39 个。不过,这些言论仍然有效。

    让我们看看会发生什么! len(df) 是 38,len(df) / 7 是 5.43,round(len(df) / 7) 是 5。您正在四舍五入到最接近的完整周。如果您的数据多包含一天,它会像您预期的那样四舍五入到 6。但是,这有点脆弱。有时它会向上取整,有时会向下取整,但您总是希望看到最后一个不完整的一周。因此,与其这样做,我将向您介绍两个不错的功能:// 运算符,它是一个地板除法(总是向下舍入)和divmod,一个内置函数,它同时进行地板除法并给出你剩下的。

    我建议的解决方案使用 divmod 来计算任何不完整的周数:

    n_weeks, remaining_days = divmod(len(df), 7)
    n_weeks += min(1, remaining_days)
    
    for i in range(n_weeks):
        ...
    

    【讨论】:

      【解决方案2】:

      您可以通过首先设置图形布局,然后将显式坐标区对象传递给 pandas 绘图方法来实现此目的。然后我有条件地只在最后一个图上显示 x 轴标签。我还删除了对日期名称的映射 - 现在直接通过绘图完成。显然,如果出于其他原因需要,可以放回原处!

      import pandas as pd
      import matplotlib.pyplot as plt
      import calendar
      
      df = pd.read_csv("health.csv", header = None, names = ['Physical', 'Emotional'])
      # Get Dayofweek index number (start with 6 for sunday) 6,0,1....
      df['DayOfTheWeek'] = [(i+6) % 7  for i in range(len(df))]
      
      df_calendar = calendar.Calendar(firstweekday=6)
      
      weeks = int(round(len(df)/7))
      fig, axes = plt.subplots(weeks, 1, figsize=(6, weeks*3))
      
      # Loop through the df (splitting week by week)
      for i in range(weeks):
          ax=axes[i]
      
          df.iloc[i*7:(i+1)*7].set_index('DayOfTheWeek').plot(kind='bar', ax=axes[i])
          ax.set_ylim([0,10])
          ax.set_xlim([-0.5,6.5])
          ax.set_xticks(range(7))
      
          if i == 0:
              ax.legend().set_visible(True)
          else:
              ax.legend().set_visible(False)
      
          if i == weeks-1:
              ax.set_xticklabels([calendar.day_name[weekday] for weekday in df_calendar.iterweekdays()])
              ax.set_xlabel("Day of the week")
          else:
              ax.set_xticklabels([])
              ax.set_xlabel("")
      
      plt.savefig("health.png")
      plt.show()
      

      【讨论】:

      • 太好了,谢谢。小型跟进:一周应该从星期日开始,因为第一行与星期日有关。另外,是否可以只在顶部子图中有图例?
      • 我已编辑以包含该更改。周日的事情是由于日历 - 我的周一开始(英国),如果你在你的机器上运行应该没问题。
      • 谢谢。我的也从星期一开始,所以这意味着目前所有的日子都错了。我的意思是数据框的第一行确实对应于星期日,第二行对应于星期一等。
      • 啊,好吧,我已经把你的映射逻辑放回去了,所以现在应该从周日开始。
      • 谢谢。我们现在已经丢失了 x 轴上的星期四、星期五、星期六和星期日的名称。
      猜你喜欢
      • 2019-03-28
      • 1970-01-01
      • 1970-01-01
      • 2014-10-01
      • 1970-01-01
      • 2021-05-17
      • 2015-04-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多