【问题标题】:Bokeh Graph for Resampled, Hierarchical, Categorical+Time Data重采样、分层、分类+时间数据的散景图
【发布时间】:2019-07-08 10:55:32
【问题描述】:

我知道我正在接近这一点,但我无法让散景来做我正在寻找的东西。我需要将时间数据重新采样为 15 分钟的间隔,然后按分层、分类类型对其进行分组,并在时间组中绘制结果图。将不胜感激任何帮助。

我的数据如下所示:

    basket_id   food_type               classified_time             dipped_time                 slot_number
0   185261      CHICKEN FILLETS         2019-07-07 11:38:23.153858  2019-07-07 11:38:40.271070  8
1   185263      CHICKEN FILLETS         2019-07-07 11:38:25.831668  2019-07-07 11:38:53.265553  4
2   185273      CRISPY CHICKEN TENDERS  2019-07-07 11:39:26.184932  2019-07-07 11:39:58.164302  5
3   185276      CRISPY CHICKEN TENDERS  2019-07-07 11:39:30.178273  2019-07-07 11:39:46.076617  1
...

我可以对这些数据进行重新采样,从而得到这个结果,看起来非常像在正确的轨道上:

agg_15m = df[['dipped_time', 'food_type']] \
            .set_index('dipped_time', 'food_type') \
            .groupby('food_type') \
            .resample('15Min') \
            .agg({'food_type': 'count'}) \
            .rename(columns={'food_type':'COUNT'}) \
            .reset_index()
display(agg_15m)

然后我可以使用 groupby 来获得我认为正确的结构:

group = agg_15m.groupby(['dipped_time', 'food_type'])
display(group.sum())

仅此一项就需要在数据帧中进行大量计算,因为我对使用多索引数据的概念并不十分熟悉。

现在是有趣的部分,尝试让 Bokeh 使用这些数据做一些事情。 This instruction from bokeh 似乎给出了正确的方向;但是,它只使用一个 groupby。 This instruction from bokeh 为分层分类数据提供了一些方向,但该示例仅使用文字完成。

这就是我尝试过的。

    p = figure(
        title="Baskets Cooked per 15min",
        y_axis_label="Count",
        plot_width=plot_width,
        plot_height=plot_height,
        toolbar_location=toolbar_loc,
    )
    p.vbar(x='dipped_time_food_type', top='COUNT', width=1e3*60*15, source=self.group.sum() )

这给出了一个空图

如果我尝试将组对象放入 x_range,as per these instructions

self.p = figure(
            title="Baskets Cooked per 15min",
            y_axis_label="Count",
            plot_width=plot_width,
            plot_height=plot_height,
            toolbar_location=toolbar_loc,
            x_range=group
        )

我在设置图形时收到以下错误,即使这似乎是the format explained here

ValueError: expected an element of either Seq(String), Seq(Tuple(String, String)) or Seq(Tuple(String, String, String)), got [(Timestamp('2019-07-07 11:30:00'), 'CHICKEN FILLETS'), (Timestamp('2019-07-07 11:30:00'), 'CRISPY CHICKEN TENDERS'), (Timestamp('2019-07-07 11:30:00'), 'POPCORN CHICKEN'), (Timestamp('2019-07-07 11:30:00'), 'POTATO FRIES'), (Timestamp('2019-07-07 11:45:00'), 'CHICKEN FILLETS'), (Timestamp('2019-07-07 11:45:00'), 'CRISPY CHICKEN TENDERS'), (Timestamp('2019-07-07 11:45:00'), 'POPCORN CHICKEN'), (Timestamp('2019-07-07 11:45:00'), 'POTATO FRIES'), (Timestamp('2019-07-07 12:00:00'), 'CHICKEN FILLETS'), (Timestamp('2019-07-07 12:00:00'), 'CRISPY CHICKEN TENDERS'), (Timestamp('2019-07-07 12:00:00'), 'POPCORN CHICKEN'), (Timestamp('2019-07-07 12:00:00'), 'POTATO FRIES'), (Timestamp('2019-07-07 12:15:00'), 'CHICKEN FILLETS'), (Timestamp('2019-07-07 12:15:00'), 'CRISPY CHICKEN TENDERS'), (Timestamp('2019-07-07 12:15:00'), 'POPCORN CHICKEN'), (Timestamp('2019-07-07 12:15:00'), 'POTATO FRIES'), (Timestamp('2019-07-07 12:30:00'), 'CHICKEN FILLETS'), (Timestamp('2019-07-07 12:30:00'), 'CRISPY CHICKEN TENDERS'), (Timestamp('2019-07-07 12:30:00'), 'POPCORN CHICKEN'), (Timestamp('2019-07-07 12:30:00'), 'POTATO FRIES'), (Timestamp('2019-07-07 12:45:00'), 'CRISPY CHICKEN TENDERS'), (Timestamp('2019-07-07 12:45:00'), 'POPCORN CHICKEN'), (Timestamp('2019-07-07 12:45:00'), 'POTATO FRIES'), (Timestamp('2019-07-07 13:00:00'), 'CRISPY CHICKEN TENDERS'), (Timestamp('2019-07-07 13:00:00'), 'POTATO FRIES'), (Timestamp('2019-07-07 13:15:00'), 'CRISPY CHICKEN TENDERS'), (Timestamp('2019-07-07 13:15:00'), 'POTATO FRIES'), (Timestamp('2019-07-07 13:30:00'), 'CRISPY CHICKEN TENDERS'), (Timestamp('2019-07-07 13:30:00'), 'POTATO FRIES'), (Timestamp('2019-07-07 13:45:00'), 'POTATO FRIES'), (Timestamp('2019-07-07 14:00:00'), 'POTATO FRIES'), (Timestamp('2019-07-07 14:15:00'), 'POTATO FRIES')]

我也尝试了其他一些方法,但这似乎是我得到的最接近的方法。希望对数据框的结构有任何见解,或者我缺少的任何其他愚蠢的错误。

感谢您的帮助!

编辑 所以我注意到最后一个错误不是关于数据结构,而是关于数据类型。所以我将日期时间转换为字符串:

agg_15m = df[['dipped_time', 'food_type']] \
                .set_index('dipped_time', 'food_type') \
                .groupby('food_type') \
                .resample('15Min') \
                .agg({'food_type': 'count'}) \
                .rename(columns={'food_type':'COUNT'}) \
                .reset_index()
agg_15m['dipped_time'] = agg_15m['dipped_time'].to_string()
self.group = agg_15m.groupby(['dipped_time', 'food_type'])
self.p = figure(
            title="Baskets Cooked per 15min",
            y_axis_label="Count",
            plot_width=plot_width,
            plot_height=plot_height,
            toolbar_location=toolbar_loc,
            x_range=self.group
        )
self.p.vbar(x='dipped_time_food_type', top='COUNT_std', width=1, source=ColumnDataSource(self.group))

这现在给了我一个相当丑陋的图表,它似乎并不代表基础数据。

我正在尝试做一些类似这样的事情:

编辑

上一版本的字符串转换不正确。更新为

agg_15m = df[['dipped_time', 'food_type']] \
                .set_index('dipped_time', 'food_type') \
                .groupby('food_type') \
                .resample('15Min') \
                .agg({'food_type': 'count'}) \
                .rename(columns={'food_type':'COUNT'}) \
                .reset_index()
agg_15m['dipped_time'] = agg_15m['dipped_time'].astype(str)
self.group = agg_15m.groupby(['dipped_time', 'food_type'])
self.p = figure(
            title="Baskets Cooked per 15min",
            y_axis_label="Count",
            plot_width=plot_width,
            plot_height=plot_height,
            toolbar_location=toolbar_loc,
            x_range=self.group
        )
self.p.vbar(x='dipped_time_food_type', top='COUNT_std', width=1, source=ColumnDataSource(self.group))

这给出了正确的数据,但现在图表是空的,角落上有一些伪影。

编辑

我无法让它工作,所以我选择了手动方法。此代码有效:

    df['dipped_time'] = pd.to_datetime(df['dipped_time'], errors='coerce') #convert to datetime so we can resample
    #group by food and resample to 15min intervals
    agg_15m = df[['dipped_time', 'food_type']] \
                .set_index('dipped_time', 'food_type') \
                .groupby('food_type') \
                .resample('15Min') \
                .agg({'food_type': 'count'}) \
                .rename(columns={'food_type':'COUNT'}) \
                .reset_index()
    agg_15m['dipped_time'] = agg_15m['dipped_time'].astype(str)
    plot_width  = 800
    plot_height = 600
    toolbar_loc = 'above'

    self.p = figure(
            title="Baskets Cooked per 15min",
            y_axis_label="Count",
            plot_width=plot_width,
            plot_height=plot_height,
            toolbar_location=toolbar_loc,
            x_range=sorted(self.agg_15m.dipped_time.unique())
        )
    self.food_types = self.agg_15m.food_type.unique()
    self.data_source = dict(
            x=sorted(self.agg_15m.dipped_time.unique())
        )
    df = self.agg_15m
    for food_type in self.food_types:
            arr = []
            for time in sorted(self.agg_15m.dipped_time.unique()):
                if df.loc[(df["dipped_time"]==time) & (df["food_type"]==food_type), "COUNT"].empty:
                    arr.append(0)
                else:
                    arr.append(df.loc[(df["dipped_time"]==time) & (df["food_type"]==food_type), "COUNT"].values[0])
            self.data_source[food_type] = arr

    fill_colors=[
            Spectral5[i]
            for i in range(len(self.food_types))]

    self.p.vbar_stack(self.food_types, \
                          x='x', \
                          width=0.9, alpha=0.5, \
                          source=ColumnDataSource(self.data_source), \
                          fill_color=fill_colors,
                          legend=[value(x) for x in self.food_types])

仍然愿意接受更多惯用的解决方案。

【问题讨论】:

标签: python dataframe bokeh hierarchical-data


【解决方案1】:

您试图将COUNT_std 绘制为条形的顶部,但如果您实际查看ColumnDataSource 中的数据,您会发现它只是 NaN 值:

 'COUNT_std': array([nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan]),

确实,如果您返回该组,查看 group.describe() 的输出,您会发现 NaN 来自那里:

In [40]: group.describe()
Out[40]:
                                           COUNT
                                           count mean std  min  25%  50%  75%  max
dipped_time         food_type
2019-07-07 12:30:00 POTATO FRIES             1.0  5.0 NaN  5.0  5.0  5.0  5.0  5.0
2019-07-07 12:45:00 CRISPY CHICKEN TENDERS   1.0  3.0 NaN  3.0  3.0  3.0  3.0  3.0
                    POPCORN CHICKEN          1.0  3.0 NaN  3.0  3.0  3.0  3.0  3.0
                    POTATO FRIES             1.0  4.0 NaN  4.0  4.0  4.0  4.0  4.0
2019-07-07 13:00:00 CRISPY CHICKEN TENDERS   1.0  6.0 NaN  6.0  6.0  6.0  6.0  6.0
                    POTATO FRIES             1.0  3.0 NaN  3.0  3.0  3.0  3.0  3.0
2019-07-07 13:15:00 CRISPY CHICKEN TENDERS   1.0  0.0 NaN  0.0  0.0  0.0  0.0  0.0
                    POTATO FRIES             1.0  5.0 NaN  5.0  5.0  5.0  5.0  5.0
2019-07-07 13:30:00 CRISPY CHICKEN TENDERS   1.0  6.0 NaN  6.0  6.0  6.0  6.0  6.0
                    POTATO FRIES             1.0  1.0 NaN  1.0  1.0  1.0  1.0  1.0
2019-07-07 13:45:00 POTATO FRIES             1.0  6.0 NaN  6.0  6.0  6.0  6.0  6.0
2019-07-07 14:00:00 POTATO FRIES             1.0  0.0 NaN  0.0  0.0  0.0  0.0  0.0
2019-07-07 14:15:00 POTATO FRIES             1.0  3.0 NaN  3.0  3.0  3.0  3.0  3.0

我不确定为什么该列最终会充满 NaN,但这是最后一个情节出现问题的直接原因。相反,如果您使用具有有效数值的列,例如COUNT_max:

p.vbar(x='dipped_time_food_type', top='COUNT_max', width=0.9, source=group)

然后你可以看到一个你所追求的情节,以任何视觉样式为模:

请注意,我将条形宽度设为 0.9,因此它们之间实际上存在空间。

【讨论】:

  • 好电话。我使用了 COUNT_std,因为错误提示它作为列名,而且我移动得太快了。不知道那是什么。我假设它是 NaN,因为您不能有一个标准开发人员,每个存储桶中只有一个值。
  • 我想我还是会选择堆叠的条形图,因为我需要将 x 轴设为实际的时间线,以便显示任何间隙。分类轴将忽略任何 15 分钟的间隙,这对我在这里强调很重要。感谢您帮助捕获该错误!与往常一样,令人沮丧的是,事情就是这么简单。
  • 实际上,我只是为了检查而进行了更新,它只是让图表完全消失了。我确定这是我机器本地的东西,但奇怪的是没有错误也没有图表。再次感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-01
  • 2022-09-30
  • 2012-12-15
  • 1970-01-01
  • 1970-01-01
  • 2015-11-21
  • 2023-01-29
相关资源
最近更新 更多