Диаграмма Ганта для данных гидрологии USGS с Python? - PullRequest
2 голосов
/ 06 февраля 2020

У меня есть скомпилированный фрейм данных, который содержит данные о потоке USGS на нескольких разных потоковых данных. Теперь я хочу создать диаграмму Ганта, похожую на this . В настоящее время мои данные имеют столбцы в качестве имен сайтов и индекс даты в виде строк.

Вот пример моих данных .

Проблема с примером диаграммы Ганта, которую я связал, состоит в том, что в моих данных есть промежутки между начальной и конечной датами, которые обычно определить горизонтальные временные линии. Многие из найденных мною примеров учитывают только дату начала и окончания, но не пропускают значения, которые могут быть между. Как я могу учесть пробелы, в которых нет данных (пробелов или нанограмм в этих слотах для значений) для некоторых сайтов?

Во-первых, у меня есть график, показывающий, где находятся отсутствующие данные.

import missingno as msno
msno.bar(dfp)

Missing Streamflow Gage Data

Теперь мне нужно время на оси x и горизонтальная линия на оси y, которая отслеживает, когда сайты содержат данные в те времена. Я знаю, как сделать это методом грубой силы, что будет означать ручное выделение даты начала и окончания, когда есть действительные данные (которые я составил ниже).

from datetime import datetime
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as dt

df=[('RIO GRANDE AT EMBUDO, NM','2015-7-22','2015-12-7'),
('RIO GRANDE AT EMBUDO, NM','2016-1-22','2016-8-5'),
('RIO GRANDE DEL RANCHO NEAR TALPA, NM','2014-12-10','2015-12-14'),
('RIO GRANDE DEL RANCHO NEAR TALPA, NM','2017-1-10','2017-11-25'),
('RIO GRANDE AT OTOWI BRIDGE, NM','2015-8-17','2017-8-21'),
('RIO GRANDE BLW TAOS JUNCTION BRIDGE NEAR TAOS, NM','2015-9-1','2016-6-1'),
('RIO GRANDE NEAR CERRO, NM','2016-1-2','2016-3-15'),
] 
df=pd.DataFrame(data=df)
df.columns = ['A', 'Beg', 'End']
df['Beg'] = pd.to_datetime(df['Beg'])
df['End'] = pd.to_datetime(df['End'])

fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111)
ax = ax.xaxis_date()
ax = plt.hlines(df['A'], dt.date2num(df['Beg']), dt.date2num(df['End']))

enter image description here

Как мне сделать фигуру (как показано выше) с кадром данных, который я привел в качестве примера? В идеале я хочу избегать метода грубой силы.

Обратите внимание: нулевые значения считаются действительными точками данных.

Заранее благодарю за отзыв!

Ответы [ 2 ]

1 голос
/ 11 февраля 2020

Найти диапазоны дат ненулевых данных

2020-02-12 Редактировать, чтобы уточнить логи c в л oop

df = pd.read_excel('Downloads/output.xlsx', index_col='date')

Убедитесь, что даты в порядке:

df.sort_index(inplace=True)

L oop через данные и найдите границы диапазонов хороших данных. Получите соответствующие значения индекса и название датчика и соберите их все в список:

# Looping feels like defeat. However, I'm not clever enough to avoid it 
good_ranges = []
for i in df:
    col = df[i]
    gauge_name = col.name

    # Start of good data block defined by a number preceeded by a NaN
    start_mark = (col.notnull() & col.shift().isnull())
    start = col[start_mark].index

    # End of good data block defined by a number followed by a Nan
    end_mark = (col.notnull() & col.shift(-1).isnull())
    end = col[end_mark].index

    for s, e in zip(start, end):
        good_ranges.append((gauge_name, s, e))

good_ranges = pd.DataFrame(good_ranges, columns=['gauge', 'start', 'end'])

Plotting

Здесь нет ничего нового. Скопировано практически прямо из вашего вопроса:

fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111)
ax = ax.xaxis_date()
ax = plt.hlines(good_ranges['gauge'], 
                dt.date2num(good_ranges['start']), 
                dt.date2num(good_ranges['end']))
fig.tight_layout()

enter image description here

1 голос
/ 07 февраля 2020

Вот подход, который вы могли бы использовать, он немного хакерский, поэтому, возможно, кто-то другой даст лучшее решение, но он должен дать желаемый результат. Сначала используйте pd.where для замены значений, отличных от NaN, на целое число, которое позже определит положение линий на оси y позже, я делаю эту строку за строкой, чтобы все данные, которые принадлежат друг другу, были на одной высоте. Если вы хотите увеличить расстояние между строками диаграммы Ганта, вы можете добавить число к i, я привел пример в комментариях в блоке кода ниже.

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

Этот подход возвращает объект matplotlib.pyplot.axes и matplotlib.pyplot.Figure, поэтому вы можете настроить эстетику диаграммы в соответствии с вашими целями (т. Е. Изменить толщину линий, цвета и т. Д. c.). Ссылка на документы.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

df = pd.read_excel('output.xlsx')
dates = pd.to_datetime(df.date)
df.index = dates
df = df.drop('date', axis=1)

new_rows = [df[s].where(df[s].isna(), i) for i, s in enumerate(df, 1)]
# To increase spacing between lines add a number to i, eg. below:
# [df[s].where(df[s].isna(), i+3) for i, s in enumerate(df, 1)]
new_df = pd.DataFrame(new_rows)

### Plotting ###

fig, ax = plt.subplots() # Create axes object to pass to pandas df.plot()
ax = new_df.transpose().plot(figsize=(40,10), ax=ax, legend=False, fontsize=20)
list_of_sites = new_df.transpose().columns.to_list() # For y tick labels
x_tick_location = new_df.iloc[:, 0].values # For y tick positions
ax.set_yticks(x_tick_location) # Place ticks in correct positions
ax.set_yticklabels(list_of_sites) # Update labels to site names

Gantt chart

...