箱线图放置在时间轴上
Posted
技术标签:
【中文标题】箱线图放置在时间轴上【英文标题】:Boxplot placed on time axis 【发布时间】:2016-11-29 07:45:37 【问题描述】:我想在时间轴上放置一系列(matplotlib)箱线图。它们是在一年中的不同日子进行的一系列测量。日期分布不均匀,我对随时间的变化感兴趣。
简易版
我有一个带有索引和一系列数字的 pandas DataFrame,或多或少像这样:(注意索引):
np.random.seed(12345)
data = np.array( [ np.random.normal( i, 1, 10 ) for i in range(3) ] )
ii = np.array([ 3, 5, 8 ] )
df = pd.DataFrame( data=data, index=ii )
对于每个索引,我都需要做一个箱线图,没问题:
plt.boxplot( [ df.loc[i] for i in df.index ], vert=True, positions=ii )
时间版本
问题是,我需要将盒子放在时间轴上,即将盒子放在具体日期
np.random.seed(12345)
data = np.array( [ np.random.normal( i, 1, 10 ) for i in range(3) ] )
dates = pd.to_datetime( [ '2015-06-01', '2015-06-15', '2015-08-30' ] )
df = pd.DataFrame( data=data, index=dates )
plt.boxplot( [ df.loc[i] for i in df.index ], vert=True )
但是,如果我合并职位:
ax.boxplot( [ df.loc[i] for i in df.index ], vert=True, positions=dates )
我收到一个错误:
TypeError:无法将“Timedelta”类型与“float”类型进行比较
查看文档显示:
plt.boxplot?
位置:类似数组,默认 = [1, 2, ..., n]
设置框的位置。刻度和限制会自动设置以匹配位置。
希望时间版本
此代码旨在澄清、缩小问题范围。框应该出现在那里,蓝点放置在下图中。
np.random.seed(12345)
data = np.array( [ np.random.normal( i, 1, 10 ) for i in range(3) ] )
dates = pd.to_datetime( [ '2015-06-01', '2015-06-15', '2015-08-30' ] )
df = pd.DataFrame( data=data, index=dates )
fig, ax = plt.subplots( figsize=(10,5) )
x1 = pd.to_datetime( '2015-05-01' )
x2 = pd.to_datetime( '2015-09-30' )
ax.set_xlim( [ x1, x2 ] )
# ax.boxplot( [ df.loc[i] for i in df.index ], vert=True ) # Does not throw error, but plots nothing (out of range)
# ax.boxplot( [ df.loc[i] for i in df.index ], vert=True, positions=dates ) # This is what I'd like (throws TypeError)
ax.plot( dates, [ df.loc[i].mean() for i in df.index ], 'o' ) # Added to clarify the positions I aim for
有没有办法在时间轴上放置箱线图?
我正在使用:
python:3.4.3 + numpy:1.11.0 + pandas:0.18.0 + matplotlib:1.5.1
【问题讨论】:
除非日期是您的列索引,您不能让它们位于 x 轴上。箱线图在 y 轴上绘制给定字段/列的范围,同时在 x 轴上保留字段/列的名称。您可以水平绘制它们。但想法保持不变。 您是否尝试传入position
的日期时间对象列表?
看起来您需要为 width
kwarg 显式传递一个增量时间
@Abdou 当然你可以在x轴上有日期,你可以直接传递它们:plt.plot( dates.dt, np.arange(12) )
@Luis 这是一个阴谋。不是箱线图!
【参考方案1】:
到目前为止,我最好的解决方案是将轴的单位转换为合适的int
单位并相应地绘制所有内容。就我而言,那是几天。
np.random.seed(12345)
data = np.array( [ np.random.normal( i, 1, 10 ) for i in range(3) ] )
dates = pd.to_datetime( [ '2015-06-01', '2015-06-15', '2015-08-30' ] )
df = pd.DataFrame( data=data, index=dates )
fig, ax = plt.subplots( figsize=(10,5) )
x1 = pd.to_datetime( '2015-05-01' )
x2 = pd.to_datetime( '2015-09-30' )
pos = ( dates - x1 ).days
ax.boxplot( [ df.loc[i] for i in df.index ], vert=True, positions=pos )
ax.plot( pos, [ df.loc[i].mean() for i in df.index ], 'o' )
ax.set_xlim( [ 0, (x2-x1).days ] )
ax.set_xticklabels( dates.date, rotation=45 )
箱线图放置在正确的位置,但代码对我来说似乎有点麻烦。
更重要的是:x轴的单位不再是“时间”了。
【讨论】:
【参考方案2】:可以通过两种方式生成所需的输出。但请记住,boxplots
在y-axis
上绘制给定字段/列的范围,同时在x-axis
上保留字段/列的名称。您可以水平绘制它们。但想法还是一样。
无论如何,您都可以使用 pandas timestamp
对象作为列名来创建数据框。这样,当您在数据框上调用 boxplot 函数时,输出将显示 x-axis
上的列名:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(12345)
data = np.array([np.random.normal(i, 1, 50) for i in range(12)])
##Create an array that will be the names of your columns
ii = pd.date_range(pd.Timestamp('2015-06-01'),periods=data.shape[1], freq='MS')
##Create the DataFrame
df = pd.DataFrame(data=data, columns=ii)
##I am going to reduce the number of columns so that the plot can show
checker = ii[:3]
df[checker].boxplot()
#Show the boxplots. This is just for 3 columns out of 50
plt.show()
您还可以通过转置数据框来使用您所拥有的内容,以便索引将成为列名。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(12345)
data = np.array([np.random.normal(i, 1, 50) for i in range(12)])
##Create an array that will be the indices of your dataframe
ii = pd.date_range(pd.Timestamp('2015-06-01'),periods=data.shape[0], freq='MS')
##Create the DataFrame
df = pd.DataFrame(data=data, index=ii)
##I am going to reduce the number of columns so that the plot can show
checker = ii[:3]
df.T[checker].boxplot()
#Show the boxplots. This is just for 3 columns out of 50
plt.show()
我希望这会有所帮助。
【讨论】:
我一直在思考你的答案,直到现在我才意识到我用不规则间隔的日期进行了全面测试。您的方法仅将日期作为标签,但实际上并未设置轴上框的 position。 很抱歉在 cmets 中另有说明,这是我的错误,但这种方法并没有回答原始问题。【参考方案3】:这是一个相当晚的答案,但我认为这仍然相关。以下是我的尝试
import numpy as np[![enter image description here][1]][1]
import pandas as pd
import matplotlib.dates as mdates
import matplotlib as mpl
import matplotlib.pyplot as plt
np.random.seed(12345)
data = np.array( [ np.random.normal( i, 1, 10 ) for i in range(3) ] )
dates = pd.to_datetime( [ '2015-06-01', '2015-06-15', '2015-08-30' ] )
df = pd.DataFrame( data=data, index=dates )
df.T.boxplot(
positions=mpl.dates.date2num(df.index),
widths=5,
)
ax = plt.gca()
f = plt.gcf()
locator = mdates.AutoDateLocator(minticks=3, maxticks=7)
formatter = mdates.ConciseDateFormatter(locator)
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)
【讨论】:
以上是关于箱线图放置在时间轴上的主要内容,如果未能解决你的问题,请参考以下文章