Обновление фрейма данных путем ссылки на значения строк и значения столбцов без итерации

#python #pandas #dataframe

#python #pandas #фрейм данных

Вопрос:

Я устал от панд и фреймов данных.

У меня есть один фрейм данных (именованный data ) с двумя столбцами ( userid , date ). У меня есть второй фрейм данных, incidence_matrix , где строки — это userid s (те же userid s в данных), а столбцы — даты (те же даты в data ). Вот как я создаю incidence_matrix :

 columns = pd.date_range(start='2020-01-01', end='2020-11-30', freq='M', closed='right')
index = data['USERID']
incidence_matrix = pd.DataFrame(index=index, columns=columns)
incidence_matrix = incidence_matrix.fillna(0)
  

Я пытаюсь выполнить итерацию по каждой userid паре ( date ,) data и, используя значения каждого идентификатора пользователя и даты, обновите соответствующую ячейку, incidence_matrix чтобы она была равна 1.

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

Как можно (или нужно) выполнить вышеуказанное?

Я сталкиваюсь с ошибками при попытке ссылаться на ячейки по имени, например, в моей попытке ниже, первый оператор печати работает, но второй оператор печати не распознает значение даты в качестве метки

 for index, row in data.iterrows():
  print(row['USERID'], row['POSTDATE'])
  print(incidence_matrix.loc[row['USERID']][row['POSTDATE']])
  

Заранее благодарю вас.

Комментарии:

1. похоже get_dummies() , что ( Docs ) в data df решает вашу проблему. У него не будет всех возможных значений даты, если это имеет значение для вас. Но должно быть достаточно простой адаптацией

Ответ №1:

Предупреждение: выбранное вами представление в реальной жизни будет довольно редким (посещения пользователей обычно следуют закону Zipf), что приводит к довольно неэффективному использованию памяти. Вам было бы лучше представлять свою частоту как высокую и тонкую DataFrame , например, вывод:

 data.groupby(['userid', data['date'].dt.to_period('M')]).count()
  

С учетом этого предостережения:

 def add_new_data(data, incidence=None):
    delta_incidence = (
        data
        .groupby(['userid', data['date'].dt.to_period('M')])
        .count()
        .squeeze()
        .unstack('date', fill_value=0)
    )
    if incidence is None:
        return  delta_incidence
    return incidence.combine(delta_incidence, np.add, fill_value=0).astype(int)
  

должен делать то, что вы хотите. Он повторно индексирует предыдущее значение incidence (если таковое имеется), так что результатом является новое DataFrame , где оси являются объединением incidence и delta_incidence .

Вот игрушечный пример для тестирования:

 def gen_data(n):
    return pd.DataFrame(
        dict(
            userid=np.random.choice('bob alice john james sophia'.split(), size=n),
            date=[
                (pd.Timestamp('2020-01-01')   v * pd.Timedelta('365 days')).round('s')
                for v in np.random.uniform(size=n)
            ],
        )
    )

# first time (no previous incidence)
data = gen_data(20)
incidence = add_new_data(data)

# new data arrives
data = gen_data(30)
incidence = add_new_data(data, incidence)
  

Комментарии:

1. @pierre_d очень интересно. Спасибо. погрузитесь в некоторые детали этого и опубликуйте здесь любые последующие вопросы.