График Боке для пересчитанных, иерархических, категориальных + временных данных - PullRequest
0 голосов
/ 08 июля 2019

Я знаю, что приближаюсь к этому, но я просто не могу заставить боке делать то, что я ищу.Мне нужно повторно выбрать временные данные в 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)

resampled data

Затем я могу использовать groupby, чтобы получить правильную структуру:

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

grouped by time and food type

Само по себе это потребовало немало времени на диаграммах данных, так как я не очень знаком с концепциями работы с многоиндексированными данными.

Теперь самое интересное - попытаться заставить Боке что-то сделать с этими данными. Эта инструкция от боке , кажется, дает правильное направление;однако, он использует только одну группу. Эта инструкция от 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() )

Это дает пустой график empty graph

Если я попытаюсь поместить объект группы в x_range, согласно этим инструкциям ,

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
        )

Я получаю следующую ошибку при настройке фигуры, хотя кажется, что это формат, описанный здесь :

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')]

I 'Мы также попробовали несколько других вещей, но это, кажется, самое близкое, что я получил.Хотелось бы получить представление о структуре данных или любую другую глупую ошибку, которую мне не хватает.

Спасибо за помощь!

Редактировать Итак, я заметил, что последняя ошибка была не о структуре данных, а о типах данных.Поэтому я преобразовал datetime в строки:

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))

Теперь это дает мне довольно уродливый график, который, кажется, не представляет базовые данные.

ugly graph

Я пытаюсь найти что-то более похожее на это: pretty graph

РЕДАКТИРОВАТЬ

Последняя версия преобразования строки была неправильной.Обновлено до

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))

Это дает правильные данные, но теперь график пуст с некоторыми артефактами в углу.empty graph with artifacts in corner

РЕДАКТИРОВАТЬ

Я не мог заставить его работать, поэтому я пошел на ручной метод.Этот код работает:

    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])

successful graph

Все еще открыт для более идиоматических решений.

1 Ответ

0 голосов
/ 08 июля 2019

Вы пытаетесь построить 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

Я не уверен, почему этот столбец заканчиваетсяполон NaNs, но это является непосредственной причиной проблем с последним сюжетом.Если вместо этого вы используете столбец с допустимыми числовыми значениями, например COUNT_max:

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

, то вы можете увидеть график, похожий на тот, который вам нужен, по модулю любого визуального стиля:

enter image description here

Обратите внимание, что я установил ширину стержня 0,9, чтобы между ними было свободное пространство.

...