Установите значение в ячейку pandas DataFrame - PullRequest
2 голосов
/ 04 марта 2020

У меня есть два кадра данных:

source_df (source_df.shape == (1008, 27797)):

|id   |field_1|  pubs  | users |...|user_1    |user_2    |user_3    |...|user_27769    |
|-----|-------|--------|-------|...|----------|----------|----------|...|--------------|
| 1   |-------|[7, 10] |[1,2,3]|...| x_1_1    | x_2_1    | x_3_1    |...| x_27769_1    |
| 2   |-------|[13, 15]|[2,10] |...| x_1_2    | x_2_2    | x_3_2    |...| x_27769_2    |
|..   |.......|........|[1,2,9]|...|..........|..........|..........|...|..............|
| 1008|-------|[1,2,13]|[7,8,9]|...| x_1_1008 | x_2_1008 | x_3_1008 |...| x_27769_1008 |

user_pub_df (user_pub_df.shape = (21, 27769)):

|id| user_1 | user_2 | user_3 |...| user_27769 |
|--|--------|--------|--------|...|------------|
| 1|   10   |   0    |   7    |...|     0      |
| 2|   0    |   0    |   0    |...|     1      |
| 3|   0    |   8    |   4    |...|     0      |
|..|   .    |   .    |   .    |...|     .      |
| 7|   13   |   1    |   6    |...|     0      |
|10|   1    |   1    |   0    |...|     0      |
|13|   1    |   1    |   0    |...|     0      |
|15|   1    |   1    |   0    |...|     19     |

Id вот id из столбца pubs из source_df.

Задача - заполнить source_df значениями из user_pub_df:

source_df.loc[1, 'user_1'] = user_pub_df.loc[7, 'user_1'] + user_pub_df.loc[10, 'user_1'] # 11
source_df.loc[1, 'user_2'] = user_pub_df.loc[7, 'user_2'] + user_pub_df.loc[10, 'user_2'] # 2
source_df.loc[1, 'user_3'] = user_pub_df.loc[7, 'user_3'] + user_pub_df.loc[10, 'user_3'] # 6
source_df.loc[2, 'user_2'] = user_pub_df.loc[13, 'user_2'] + user_pub_df.loc[15, 'user_2'] # 2
source_df.loc[2, 'user_10'] = user_pub_df.loc[13, 'user_10'] + user_pub_df.loc[15, 'user_10'] # 0
# and so on

Я сделал это с l oop:

for index, row in source_df.iterrows():
    for user_id in row['users']:
        source_df.loc[index, 'user_{}'.format(user_id)] = user_pub_df.loc[row['pubs'], user_id].sum()

Наивный код работает слишком медленно для 27769 пользователей и 21 паба (~ 16 минут).

Я пытался изменить .loc на .at то же самое результат.

PS: source_df можно изменить, поэтому я не могу просто сохранить все комбинации user / pubs в словаре / хэш-карте с ключом user+pubs и предварительно вычисленным значением.

Ответы [ 3 ]

0 голосов
/ 04 марта 2020

Я настроил код для использования массива numpy в l oop, а затем в конце установил его в массив данных. Это пропускает большую часть проверки индекса et c, которая происходит в pandas фреймах данных. Я думаю, что и мой метод один и два будут быстрее, но метод два должен работать лучше с большим количеством пользователей

import numpy as np
n_users = user_pub_df.shape[1]
n_rows = source_df.shape[0]
arr = np.zeros((n_rows, n_users))
for index, row in source_df.iterrows():
    for user_id in row['users']:
        arr[index, user_id] = user_pub_df.iloc[row['pubs'], user_id].sum()

source_df.loc[:, 'user_1': 'user_' + str(n_users)] = arr

Вот мой тестовый код:

import pandas as pd
import numpy as np
import numpy.ma as ma
import timeit

source_df = pd.DataFrame({
    'pubs': [[1,2],[0,2],[1,0]],
    'users': [[1,2],[0,2],[1,0]],
    'user_1': [1,2,3],
    'user_2': [1,2,3],
    'user_3': [3,2,1]
    })

user_pub_df = pd.DataFrame({
    'user_1': [1,2,3],
    'user_2': [1,2,3],
    'user_3': [3,2,1]
    })
n_users = user_pub_df.shape[1]
n_rows = source_df.shape[0]

def one() :
    global source_df
    arr = []
    for index, row in source_df.iterrows():
        mx = np.ones((len(row['pubs']), n_users))
        mx[:,row['users']] = 0
        arr.append(ma.masked_array(user_pub_df.iloc[row['pubs'],:].values,mask = mx).sum())

    source_df.loc[:, 'user_1': 'user_' + str(n_users)] = arr
    source_df = source_df.fillna(0).copy()

def two() :
    arr = np.zeros((n_rows, n_users))
    for index, row in source_df.iterrows():
        for user_id in row['users']:
            arr[index, user_id] = user_pub_df.iloc[row['pubs'], user_id].sum()

    source_df.loc[:, 'user_1': 'user_' + str(n_users)] = arr

def old() :
    for index, row in source_df.iterrows():
        for user_id in row['users']:
            source_df.loc[index, 'user_{}'.format(user_id)] = user_pub_df.iloc[row['pubs'], user_id].sum()

print(timeit.timeit(old, number =1000))
print(timeit.timeit(one, number =1000))
print(timeit.timeit(two, number =1000))

Результаты таковы:

5,25 мс

3,83 мс

3,65 мс

0 голосов
/ 04 марта 2020

Ваша структура данных предотвращает любую векторизацию, вы не можете ожидать полноскоростных операций: - (.

Лучшее, что вы можете попробовать, - это напрямую использовать базовые массивы numpy, чтобы избежать pandas для создания нового Серии для каждой строки:

for i, index in enumerate(source_df.index):
    for user_id in df['users'].values[i]:
        source_df.loc[index, 'user_{}'.format(user_id)] = user_pub_df.loc[df['pubs'].values[i],
                                                                          user_id].sum()

Но я не должен ожидать от нее слишком многого ...

0 голосов
/ 04 марта 2020

Если вы используете df.ilo c (индекс) вместо df.lo c (индекс), он должен быть быстрее

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