箱线图放置在时间轴上

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】:

可以通过两种方式生成所需的输出。但请记住,boxplotsy-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)

【讨论】:

以上是关于箱线图放置在时间轴上的主要内容,如果未能解决你的问题,请参考以下文章

更改 seaborn 箱线图中的 X 轴标签

向 Pandas DataFrame 箱线图添加图例

在箱线图中的 y 轴上添加中断

在连续 x 轴上按组填充和闪避箱线图

使用多个数据框的第一行的第一个元素创建箱线图

根据中值对箱线图进行排序