[英]How to get pandas to plot on the same graph with the same y axis range
I am trying to plot multiple bar charts vertically on top of each other. 我试图将多个条形图彼此垂直地绘制。 There should be one labelled x axis (with the days of the week). 应该有一个标记的x轴(带有星期几)。 The code I have so far is: 到目前为止,我的代码是:
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()
This has the following problems: 这有以下问题:
The full input data is: 完整的输入数据为:
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
When you call plt.ylim()
, it will "set the y-limits of the current axes.". 当您调用plt.ylim()
,它将“设置当前轴的y极限”。 It does this by calling plt.gca
under the hood , which will "Get the current Axes instance (...), or create one.". 它通过在hood下调用plt.gca
完成此操作,这将“获取当前的Axes实例(...),或创建一个实例。”。 Now, in the first iteration of your loop, no Axes exists, so it creates a new one. 现在,在循环的第一次迭代中,不存在轴,因此它创建了一个新轴。 Then pandas.DataFrame.plot proceeds to create its own figure, ignoring the existing one. 然后pandas.DataFrame.plot继续创建自己的图形,而忽略现有图形。 That's how you get an empty first plot. 这就是您获得空的第一个情节的方式。
The fix is simple: Swap the order of plt.ylim([0,10])
and the following line, or set it directly in .plot(kind='bar', ylim=(0, 10))
. 解决方法很简单:交换plt.ylim([0,10])
和以下行的顺序,或直接在.plot(kind='bar', ylim=(0, 10))
。
Perhaps plt.subplots()
is what you're looking for? 也许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()
Try printing the ranges you select in your loop, and you should be able to spot the error. 尝试打印您在循环中选择的范围,您应该能够发现错误。 It is an off-by-one error :-) 这是一个错误的错误 :-)
Spoiler/solution below! 扰流板/解决方案如下!
for i in range(int(round(len(df)/7))):
print(df.iloc[i*7:(i+1)*7])
shows that you are only selecting complete weeks. 显示您仅选择完整的星期。
Note: In copying the data from the question, I apparently missed a row! 注意:从问题中复制数据时,我显然错过了一行! There should be 39. The remarks still stand, though. 应该是39。但是这些话仍然存在。
Let's inspect what happens! 让我们检查发生了什么! len(df)
is 38, len(df) / 7
is 5.43, and round(len(df) / 7)
is 5. You are rounding down to nearest complete week. len(df)
为38, len(df) / 7
为5.43, round(len(df) / 7)
为5。您将舍入到最接近的整周。 Had your data contained one more day, it would round up to 6 as you expect. 如果您的数据再包含一天,那么您可以将其舍入为6天。 However, that is somewhat brittle behaviour; 但是,这有点易碎。 sometimes it rounds up, sometimes down, but you always want to see the last incomplete week. 有时它会四舍五入,有时会下移,但是您总是希望看到最后一个不完整的星期。 So rather than doing that, I'll introduce you to two nice features: the //
operator, which is a floor division (always rounding down), and divmod , a built-in function that simultaneously does floor division and gives you the remainder. 因此, 除此以外 ,我将向您介绍两个不错的功能: //
运算符,它是一个楼层分割(总是四舍五入),以及divmod ,它是一个内置函数,它同时进行楼层分割并为您提供余数。
My suggested solution uses divmod to count any incomplete weeks: 我建议的解决方案使用divmod来计算任何不完整的星期:
n_weeks, remaining_days = divmod(len(df), 7)
n_weeks += min(1, remaining_days)
for i in range(n_weeks):
...
You can do this by first setting up your figure layout, then passing an explicit axes object to the pandas plot method. 为此,您可以先设置图形布局,然后将显式轴对象传递给pandas plot方法。 I then conditionally only show the x axis labels on the last plot. 然后,我有条件地仅在最后一个图上显示x轴标签。 I also removed the mapping to the names of the days - this is done now via the plot directly. 我还删除了到日期名称的映射-现在可以直接通过绘图完成。 Obviously can be put back in if needed for other reasons! 显然,如果出于其他原因需要,可以放回去!
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()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.