Передача среза .loc [date] в диаграмму альтаира имеет странные результаты в зависимости от даты среза - PullRequest
1 голос
/ 20 июня 2019

пытался изобразить несколько слоев диаграммы , прежде чем я понял, что спецификация слоя не была проблемой, но то, что каким-то образом срез, который я прохожу по диаграмме, действует (для меня) странно.Если он не сломан, то я должен неправильно понять, как все должно работать.

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

import altair as alt
alt.renderers.enable('notebook')

import pandas as pd

idx = pd.IndexSlice

history_index = pd.date_range(start="31jan2016", end="30jun2019", freq="M")
forecast_index = pd.date_range(start="31jan2019", end="31dec2019", freq="M")

history_df = pd.DataFrame([z for z in range(len(history_index))], index=history_index,columns = ['history'])
forecast_df = pd.DataFrame([z for z in range(len(forecast_index))], index=forecast_index, columns = ['forecast'])

df = history_df.join(forecast_df, how="outer")
df.index.name = "date"

Первый пример работает:

#without making it a seasonal chart,  this works
non_seasonal  = alt.Chart(df.loc[idx['20170701':],:].reset_index(), title=f"non seasonal plot").mark_line().encode(
        x='date',
        y=alt.Y(f'forecast', scale=alt.Scale(zero=False)),
    )
non_seasonal

enter image description here

Но когда я начинаю превращать их в сезонные графики, делая месяц оси X, возникают проблемы.

Мой первый срез работает, я просто нарезаю все существующие forecast данные, которые начинаются в январе2019 года.

#works ok: shows all the data since 1jan2019
seasonal1 = alt.Chart(df.loc[idx['20190101':],:].reset_index(), title=f"seasonal plot").mark_line().encode(
        x='month(date)',
        y=alt.Y(f'forecast', scale=alt.Scale(zero=False)),
    )
seasonal1

enter image description here

Но когда я начинаю нарезку с более ранних дат (до того, как «прогноз» получит какие-либо данные), у меня возникают проблемы.

#fails:  shows no data
seasonal2 = alt.Chart(df.loc[idx['20180101':],:].reset_index(), title=f"seasonal plot").mark_line().encode(
        x='month(date)',
        y=alt.Y(f'forecast', scale=alt.Scale(zero=False)),
    )
seasonal2

enter image description here

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

#works if I add a color-encoding
seasonal3 = alt.Chart(df.loc[idx['20180101':],:].reset_index(), title=f"seasonal plot").mark_line().encode(
        x='month(date)',
        y=alt.Y(f'forecast', scale=alt.Scale(zero=False)),
    color="year(date):N"
    )
seasonal3

enter image description here

В этот момент все становится очень странным.Если я начну свой слайс где-нибудь в 2018 году, то вместо этого «начало» слайса будет действовать как «конец» слайса ....

#fails bizarrely -- the 20180701 slice appears to be the END of the slice, not the start
seasonal4 = alt.Chart(df.loc[idx['20180701':],:].reset_index(), title=f"seasonal plot").mark_line().encode(
        x='month(date)',
        y=alt.Y(f'forecast', scale=alt.Scale(zero=False)),
    )
seasonal4

enter image description here

Опять же, это работает, если я назначу ему цветовую кодировку

#again, it works if I add a color encoding.
seasonal5 = alt.Chart(df.loc[idx['20180701':],:].reset_index(), title=f"seasonal plot").mark_line().encode(
        x='month(date)',
        y=alt.Y(f'forecast', scale=alt.Scale(zero=False)),
        color="year(date):N"
    )
seasonal5

enter image description here

Итак, очевидный способ обойти этодобавить цветовую кодировку.Но это не сработает для меня, так как я пытаюсь наложить несколько наборов данных на этом графике (исторические данные окрашены по годам) и прогнозные данные жестко закодированы в красный цвет.

==================================================

Основываясь на ответе Джейка ниже, я достигаю желаемого конечного продукта:

forecast = alt.Chart(df.loc[idx['20180101':],'forecast'].reset_index().dropna(), title=f"seasonal plot").mark_line(color="green").encode(
        x='month(date)',
        y=alt.Y(f'forecast', scale=alt.Scale(zero=False)),
    )

history = alt.Chart(df.loc[idx['20170101':],'history'].reset_index().dropna(), title=f"seasonal plot").mark_line().encode(
        x='month(date)',
        y=alt.Y(f'history', scale=alt.Scale(zero=False)),
        color="year(date):O"
    )

forecast+history

enter image description here

1 Ответ

2 голосов
/ 20 июня 2019

Если вы измените mark_line() на mark_point(), вы увидите, что данные действительно есть, но они не отображаются на линейном графике.Зачем?Поскольку линия рисуется только между соседними ненулевыми точками.

Посмотрите на вывод df.loc[idx['20180101':],:]: вы увидите, что он содержит много строк, в основном со значениями NaN.Когда вы извлекаете месяц из индекса, эти значения NaN чередуются между определенными значениями с соответствующими месяцами, что создает разрывы в строке: в некоторых случаях разрывов так много, что больше нет соседних ненулевых точек длясоединяются, и поэтому линия не рисуется.

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

Чтобы исправить это, я бы посоветовал быть более осторожным с тем, как вы нарезаете свои данные и / или фильтруете значения NaN срезов.вы создаете.Например, на вашем seasonal2 графике вы можете сделать следующее:

df_sliced = df.loc[idx['20180101':],:].dropna().reset_index()
seasonal2 = alt.Chart(df_sliced, title=f"seasonal plot").mark_line().encode(
        x='month(date)',
        y=alt.Y(f'forecast', scale=alt.Scale(zero=False)),
    )
seasonal2

enter image description here

Другой вариант - использовать yearmonth вместоmonth при извлечении дат, что предотвращает перемежение неопределенных данных с определенными данными:

seasonal2 = alt.Chart(df.loc[idx['20180101':],:].reset_index(), title=f"seasonal plot").mark_line().encode(
        x='yearmonth(date)',
        y=alt.Y(f'forecast', scale=alt.Scale(zero=False)),
    )
seasonal2

enter image description here

Другие примеры можно исправитьаналогичным образом.

...