如何在 Seaborn distplot 中绘制 Pandas 日期时间序列?

Posted

技术标签:

【中文标题】如何在 Seaborn distplot 中绘制 Pandas 日期时间序列?【英文标题】:How to plot Pandas datetime series in Seaborn distplot? 【发布时间】:2016-11-28 18:32:14 【问题描述】:

我有一个带有 datetime 列的 pandas 数据框。我想根据该日期列绘制行的分布,但我目前遇到了一个无用的错误。我有:

df['Date'] = pd.to_datetime(df['Date'], errors='raise')
s = sns.distplot(df['Date'])

引发错误:

TypeError: ufunc add cannot use operands with types dtype('<M8[ns]') and dtype('<M8[ns]')

如果我将要绘制的列更改为数字数据,那么一切正常。我怎样才能让 datetime 列表现得很好?我在文档中找不到太多关于我认为我需要的内容。任何和所有帮助表示赞赏。

以下是df.head(2)的结果,出于安全等原因,我删除了一些列:

               Date                 
2812         2016-03-05
2813         2016-03-05

显然该列(当作为一个系列时)具有属性

Name: Date, dtype: datetime64[ns]

【问题讨论】:

是在抱怨格式问题,好像... 可以发一下dfhead吗? 已经进行了编辑,希望这就是您想要的。 是的。看起来 distplot 无法处理日期时间对象 stanford.edu/~mwaskom/software/seaborn/generated/… 但是,如果您的值是天,您可以 1. 将每个日期转换为 .timetuple(),2. import time,3. 将其转换为十进制 time.mktime() 感谢您的帮助。它在哪里说它不做日期时间? 它没有。将其链接为我们讨论的官方参考。 【参考方案1】:

我自己遇到同样的问题时遇到了这个问题。如 cmets 中所述,seaborn 的 distplot 似乎不支持使用日期。不幸的是,我在官方文档中找不到任何支持这一说法的内容。

我找到了两种方法来解决这个问题。它们都不是完美的,但这是我发现的最好的。

选项 1:将日期转换为数字

转换为一些数字指标并使用它。 displot 使用数字,所以如果每个日期都用数字表示,我们就可以了。日期和数字之间的映射有点像使用 MinMax Scaler。例如,我们可以将“2017-01-01”设置为 0,将“2020-06-06”设置为 1,并将它们之间的所有日期映射到 [0,1] 范围内的值。

使用的数字范围取决于您的数据范围,可能是天/月/年等。

我将通过这个玩具示例演示这种方法。

import pandas as pd
import datetime as dt

original_dates = ["2016-03-05", "2016-03-05", "2016-02-05", "2016-02-05", "2016-02-05", "2014-03-05"]
dates_list = [dt.datetime.strptime(date, '%Y-%m-%d').date() for date in original_dates]

df = pd.DataFrame("Date":dates_list)

现在数据框如下:

         Date
0  2016-03-05
1  2016-03-05
2  2016-02-05
3  2016-02-05
4  2016-02-05
5  2014-03-05

(当然不是将日期输入数据框的最佳方式,但无论如何都无所谓)。

现在我创建一个新列来保存最短日期之间的天数差异:

df["NewDate"] = df["Date"] - dt.date(2014,3,5)
df["NewDate"] = df["NewDate"].apply(lambda x: x.days)

结果:

         Date  NewDate
0  2016-03-05      731
1  2016-03-05      731
2  2016-02-05      702
3  2016-02-05      702
4  2016-02-05      702
5  2014-03-05        0

请注意,我“硬编码”了最短日期。您可以使用更好的方法来找到最小值而不是硬编码它。我只是想尽快得到这部分。

现在我们可以在我们的新专栏中使用displot

import seaborn as sns
sns.set()
ax = sns.distplot(df['NewDate'])

输出:

如您所见,它显示的是日期而不是日期。对于我个人的问题,以这种方式展示它是可以的。如果您想将其显示为日期,则需要一些额外的步骤:Show xticks which are function of x-axis, not directly the data it self. Example with dates (pandas, matplotlib)

正如我之前所说,我使用按天差进行缩放,但您可以对月或年执行相同的操作。取决于数据。

选项 2:直接使用直方图,不使用 seaborn 的分布

在这个问题中:Can Pandas plot a histogram of dates? 有一个答案 how to plot histogram with dates, using pandas's groupby.

它与displot 不同,但它可以是足够接近的解决方案(因为 displot 最终基于 matplotlib 的 hist)。

【讨论】:

【参考方案2】:

您可以将日期转换为分类类型,并绘制结果代码(整数)。然后,用日期(作为类别)标记 x-ticks。

import pandas as pd
import seaborn as sns

original_dates = [
    "2016-03-05", "2016-03-05", "2016-02-05",
    "2016-02-05", "2016-02-05", "2014-03-05"]
dates_list = pd.to_datetime(original_dates)

df = pd.DataFrame("Date": dates_list)
df['date-as-cat'] = df['Date'].astype('category')  # new 
df['codes'] = df['date-as-cat'].cat.codes          # new 

print(df)
print(df.dtypes)

        Date date-as-cat  codes
0 2016-03-05  2016-03-05      2
1 2016-03-05  2016-03-05      2
2 2016-02-05  2016-02-05      1
3 2016-02-05  2016-02-05      1
4 2016-02-05  2016-02-05      1
5 2014-03-05  2014-03-05      0

Date           datetime64[ns]
date-as-cat          category
codes                    int8
dtype: object 

date-as-code 和 date-as-category 信息的获取方式如下:

x = df[['codes', 'date-as-cat']].drop_duplicates().sort_values('codes')
print(x)

   codes date-as-cat
5      0  2014-03-05
2      1  2016-02-05
0      2  2016-03-05

【讨论】:

以上是关于如何在 Seaborn distplot 中绘制 Pandas 日期时间序列?的主要内容,如果未能解决你的问题,请参考以下文章

Python Seaborn:在 Facetgrid 中绘制多个 distplot

如何在 seaborn distplot 的 bin 中心标记 xticks?

seaborn distplot / displot 具有多个分布

无法在 seaborn distplot 中显示图例

Python Seaborn Distplot Y 值对应于给定的 X 值

如何规范seaborn distplot?