Эффективный способ перестроить словарь фреймов данных - PullRequest
3 голосов
/ 03 августа 2020

У меня есть словарь, заполненный несколькими фреймами данных. Сейчас я ищу эффективный способ изменения ключевой структуры, но решение, которое я нашел, довольно медленное, когда задействовано больше фреймов данных / больших фреймов данных. Вот почему я хотел спросить, может ли кто-нибудь знать более удобный / эффективный / быстрый способ или подход, чем мой. Итак, сначала я создал этот пример, чтобы показать, с чего я изначально начал:

import pandas as pd
import numpy as np

# assign keys to dic
teams = ["Arsenal", "Chelsea", "Manchester United"]
dic_teams = {}

# fill dic with random entries
for t1 in teams:

    dic_teams[t1] = pd.DataFrame({'date': pd.date_range("20180101", periods=30), 
                                  'Goals': pd.Series(np.random.randint(0,5, size = 30)),
                                  'Chances': pd.Series(np.random.randint(0,15, size = 30)),
                                  'Fouls': pd.Series(np.random.randint(0, 20, size = 30)),
                                  'Offside': pd.Series(np.random.randint(0, 10, size = 30))})

    dic_teams[t1] = dic_teams[t1].set_index('date')
    dic_teams[t1].index.name = None

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

# prepare lists for looping
dates = dic_teams["Arsenal"].index.to_list()
perf = dic_teams["Arsenal"].columns.to_list()
dic_dates = {}

# new structure where key = date
for d in dates:
    dic_dates[d] = pd.DataFrame(index = teams, columns = perf)

    for t2 in teams:
        dic_dates[d].loc[t2] = dic_teams[t2].loc[d]

Поскольку я использую вложенный l oop, реструктуризация моего словаря медленный. Есть ли у кого-нибудь идеи, как я могу улучшить вторую часть кода? Я не обязательно ищу только решение, а также лог c или идею, как сделать лучше.

Заранее спасибо, любая помощь приветствуется

1 Ответ

1 голос
/ 03 августа 2020

Создание Pandas фреймов данных вашим способом (как ни странно) ужасно медленным, равно как и прямая индексация .

Копирование фрейма данных происходит на удивление довольно быстро. Таким образом, вы можете использовать пустой ссылочный фрейм данных, скопированный несколько раз. Вот код:

dates = dic_teams["Arsenal"].index.to_list()
perf = dic_teams["Arsenal"].columns.to_list()
zygote = pd.DataFrame(index = teams, columns = perf)
dic_dates = {}

# new structure where key = date
for d in dates:
    dic_dates[d] = zygote.copy()

    for t2 in teams:
        dic_dates[d].loc[t2] = dic_teams[t2].loc[d]

Это примерно в 2 раза быстрее, чем эталон на моей машине.

Преодолеть медленную прямую индексацию фреймов данных сложно. Для этого мы можем использовать numpy. Действительно, мы можем преобразовать фрейм данных в массив 3D numpy, использовать numpy для выполнения транспонирования и, наконец, снова преобразовать срезы в фреймы данных. Обратите внимание, что этот подход предполагает, что все значения являются целыми числами и что фрейм входных данных хорошо структурирован .

Вот окончательная реализация:

dates = dic_teams["Arsenal"].index.to_list()
perf = dic_teams["Arsenal"].columns.to_list()
dic_dates = {}

# Create a numpy array from Pandas dataframes
# Assume the order of the `dates` and `perf` indices are the same in all dataframe (and their order)
full = np.empty(shape=(len(teams), len(dates), len(perf)), dtype=int)
for tId,tName in enumerate(teams):
    full[tId,:,:] = dic_teams[tName].to_numpy()

# New structure where key = date, created from the numpy array
for dId,dName in enumerate(dates):
    dic_dates[dName] = pd.DataFrame({pName: full[:,dId,pId] for pId,pName in enumerate(perf)}, index = teams)

Эта реализация в 6,4 раза быстрее чем эталон на моей машине. Обратите внимание, что около 75% времени, к сожалению, тратится на звонки pd.DataFrame. Таким образом, если вам нужен более быстрый код, используйте базовый c 3D numpy массив !

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...