如何让pandas在相同的y轴范围内绘制在相同的图形上

时间:2017-11-23 11:21:53

标签: python pandas matplotlib

我试图将多个条形图垂直地绘制在一起。应该有一个标记为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个点。
  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
    

2 个答案:

答案 0 :(得分:3)

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):
    ...

答案 1 :(得分:2)

您可以先设置图形布局,然后将显式轴对象传递给pandas plot方法。然后我有条件地仅在最后一个图上显示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()

health