кластеризация нескольких категориальных столбцов для построения графика временных рядов в matplotlib

#python #pandas #matplotlib #seaborn

#python #pandas #matplotlib #seaborn

Вопрос:

Меня интересует, как пандемия COVID влияет на мясоперерабатывающие предприятия по всей стране. Я извлек данные NYT по COVID на уровне округа и статистические данные из агентства по продовольствию. Здесь я исследую, как растут случаи заболевания COVID в округах, где расположены крупные предприятия пищевой промышленности, потому что большее количество больных сотрудников на заводах может оказать негативное влияние на бизнес. В моей первой попытке я вычислил графики временных рядов со скользящим средним, на которых новые случаи COVID по сравнению с 7 днями в среднем по дате.

Но, я думаю, было бы более эффективно, я мог бы заменить график, который представляет num-emp и new-cases по округам в цикле for. Для достижения этого, я думаю, было бы лучше сгруппировать их по уровню компании и развернуть их на несколько графиков, чтобы линии не перекрывались и не становились трудноразличимыми. Я не уверен, как добиться этого с помощью моей текущей попытки. Кто-нибудь может предложить возможные способы сделать это в matplotlib? Есть идея?

моя текущая попытка:

Вот воспроизводимые данные в этом gist, которые я использовал в своем эксперименте:

импортируйте pandas как pd, импортируйте matplotlib.pyplot как plt, импортируйте matplotlib.dates как mdates, импортируйте seaborn как sns из datetime, импортируйте timedelta, datetime

 df = pd.read_csv("https://gist.githubusercontent.com/jerry-shad/7eb2dd4ac75034fcb50ff5549f2e5e21/raw/477c07446a8715f043c9b1ba703a03b2f913bdbf/covid_tsdf.csv")
df.drop(['Unnamed: 0', 'fips', 'non-fed-slaughter', 'fed-slaughter', 'total-slaughter', 'mcd-asl'], axis=1, inplace=True)
for ct in df['county_state'].unique():
    dd = df[df['county_state'] == ct].groupby(['county_state', 'date', 'est'])[['cases','new_cases']].sum().unstack().reset_index()
    dd.columns= ['county_state','date', 'cases', 'new_cases']
    dd['date'] = pd.to_datetime(dd['date'])
    dd['rol7'] = dd[['date','new_cases']].rolling(7).mean()
    fig = plt.figure(figsize=(8,6),dpi=144)
    ax = fig.add_subplot(111)
    colors = sns.color_palette()
    ax2 = ax.twinx()
    ax = sns.lineplot('date', 'rol7', data=dd, color=colors[1], ax=ax)
    ax2 = sns.lineplot('date', 'cases', data=dd, color=colors[0], ax=ax2)
    ax.set_xlim(dd.date.min(), dd.date.max())
    fig.legend(['rolling7','cases'],loc="upper left", bbox_to_anchor=(0.01, 0.95), bbox_transform=ax.transAxes)
    ax.grid(axis='both', lw=0.5)
    locator = mdates.AutoDateLocator()
    ax.xaxis.set_major_locator(locator)
    fig.autofmt_xdate(rotation=45)
    ax.set(title=f'US covid tracking in meat processing plants by county - Linear scale')
    plt.show()
  

вот мой текущий результат:

введите описание изображения здесь

но этот результат не совсем важен для понимания того, как COVID влияет на компанию по переработке продуктов питания из-за зараженных сотрудников. Чтобы сделать это более визуально доступным для понимания, я думаю, мы можем заменить два графика на num-emp и недавно зараженный случай new_cases и нарисовать округа, которые нам нужны в процессе цикла. На этом этапе было бы лучше сгруппировать их по характеристикам компании и т.д. И разложить их на несколько графиков, чтобы линии не перекрывались и их было трудно увидеть. Я хочу создать EDA, который предоставляет такого рода информацию визуально. Кто-нибудь может предложить возможные способы сделать это с matplotlib ? Есть мысли? Спасибо!

Ответ №1:

  • Было несколько проблем, я сделал встроенные обозначения
  • Основная проблема заключалась в .groupby
    • Данные уже выбраны, 'country_state' поэтому в groupby этом нет необходимости
    • Только reset_index(level=1) сохраняйте дату в индексе для переноса
    • .unstack() создавал многоуровневые имена столбцов.
  • Набор ci=None для построения графика.
  • Это не имеет смысла использовать 'num-emp' в качестве метрики. Он постоянен во времени.
    • Если вы хотите увидеть график, поменяйте 'cases' местами в цикле, для 'num-emp' .
  • Я думаю, что лучший способ увидеть влияние COVID на данную компанию — это найти набор данных с доходом.
  • Поскольку предприятия по переработке пищевых продуктов считаются критически важной инфраструктурой, вероятно, не произойдет значительных изменений в их численности персонала, и любой, кто болен, вероятно, находится в отпуске по болезни или увольнении.
 import pandas as pd
import matplotlib.pyplot as plt

url = 'https://gist.githubusercontent.com/jerry-shad/7eb2dd4ac75034fcb50ff5549f2e5e21/raw/477c07446a8715f043c9b1ba703a03b2f913bdbf/covid_tsdf.csv'

# load the data and parse the dates
df = pd.read_csv(url, parse_dates=['date'])

# drop unneeded columns
df.drop(['Unnamed: 0', 'fips', 'non-fed-slaughter', 'fed-slaughter', 'total-slaughter', 'mcd-asl'], axis=1, inplace=True)

for ct in df['county_state'].unique():
    
    # groupby has been updated: no need for county becasue they're all the same, given the loop; keep date in the index for rolling
    dd = df[df['county_state'] == ct].groupby(['date', 'est', 'packer'])[['cases','new_cases']].sum().reset_index(level=[1, 2])
    dd['rol7'] = dd[['new_cases']].rolling(7).mean()

    colors = sns.color_palette()
    
    fig, ax = plt.subplots(figsize=(8, 6), dpi=144)
    ax2 = ax.twinx()
    
    sns.lineplot(dd.index, 'rol7', ci=None, data=dd, color=colors[1], ax=ax)  # date is in the index
    sns.lineplot(dd.index, 'cases', ci=None, data=dd, color=colors[0], ax=ax2)  # date is in the index
    
    ax.set_xlim(dd.index.min(), dd.index.max())  # date is in the index
    fig.legend(['rolling7','cases'], loc="upper left", bbox_to_anchor=(0.01, 0.95), bbox_transform=ax.transAxes)
    
    # set y labels
    ax.set_ylabel('7-day Rolling Mean')
    ax2.set_ylabel('Current Number of Cases')
    
    ax.grid(axis='both', lw=0.5)
    locator = mdates.AutoDateLocator()
    ax.xaxis.set_major_locator(locator)
    fig.autofmt_xdate(rotation=45)
    
    # create a dict for packer and est
    vals = dict(dd[['packer', 'est']].reset_index(drop=True).drop_duplicates().values.tolist())
    
    # create a custom string from vals, for the title
    insert = ', '.join([f'{k}: {v}' for k, v in vals.items()])

#     ax.set(title=f'US covid tracking in meat processing plants for {ct} nPacker: {", ".join(dd.packer.unique())}nEstablishments: {", ".join(dd.est.unique())}')

    # alternate title based on comment request
    ax.set(title=f'US covid tracking in meat processing plants for {ct} n{insert}')
    
    plt.savefig(f'images/{ct}.png')  # save files by ct name to images directory
    plt.show()
    plt.close()
  

введите описание изображения здесь