Панды, найдите первого, второго и третьего предпочтительных авторов для каждого пользователя - PullRequest
2 голосов
/ 06 мая 2019

У меня есть пандас датафрейм вида

 userID      author_cat  vote 
 234         1246        5.0
 121         2954        3.2
 234         1246        2.1
 121         2954        1.4
 234         1578        3.3
 234         1246        4.5
 121         2954        1.1
 121         9341        3.2

Ожидается

 userID      author_cat  vote  first_author     second_author   third_author
 234         1246        5.0   1246             1578            0
 121         2954        3.2   2954             9341            0
 234         1246        2.1   1246             1578            0
 121         2954        1.4   2954             9341            0
 234         1578        3.3   1246             1578            0
 234         1246        4.5   1246             1578            0
 121         2954        1.1   2954             9341            0
 121         9341        3.2   2954             9341            0

Я хочу для каждого пользователя построить три столбца на основе их рейтингов. Автор, который появляется больше всего (больше книг, написанных одним и тем же автором, рецензированных одним пользователем), должен появиться в столбце «first_preferred_author» (категориальный, например, 1246). То же самое для второго и третьего предпочтительных авторов. Если автор не существует (первый, второй и третий или даже все три, три новых столбца должны содержать 0).

Пока мне удалось получить порядок появления авторов по количеству отзывов пользователей, с:

df_new.groupby('userID')['author_cat'].value_counts()

но я не знаю, что делать отсюда.

РЕДАКТИРОВАТЬ:

Приведенная выше команда возвращает pd.series.series (?), Который выглядит следующим образом (для реального набора данных):

userID  author_cat
243     42994.0       6
        48986.0       5
        72473.0       3
        2505.0        2
        4371.0        2
        5270.0        2
        18764.0       2
        41267.0       2
        75183.0       2
        82066.0       2
        486.0         1
        571.0         1
        3507.0        1
        6343.0        1
        6524.0        1
        7530.0        1
        10539.0       1
        10679.0       1
        21123.0       1
        30948.0       1
        41305.0       1
        41479.0       1
        43715.0       1
        49236.0       1
        52183.0       1
        53204.0       1
        56812.0       1
        56916.0       1
        57911.0       1
        62266.0       1
                     ..
278633  54862.0       1
        57422.0       1
        66639.0       1
        72642.0       1
        72712.0       1
        73809.0       1
        82105.0       1
        87066.0       1
        91189.0       1
        93458.0       1
        94608.0       1
        96674.0       1
        99025.0       1
278843  80852.0       3
        3377.0        2
        4371.0        2
        2437.0        1
        2528.0        1
        5216.0        1
        18370.0       1
        30948.0       1
        35706.0       1
        37463.0       1
        40577.0       1
        62175.0       1
        62645.0       1
        81508.0       1
        92206.0       1
        92242.0       1
        93801.0       1
Name: author_cat, Length: 96302, dtype: int64

Ответы [ 4 ]

3 голосов
/ 06 мая 2019

Попробуйте это:

(df.groupby(['userID']).author_cat
   .apply(lambda x: pd.Series(x.value_counts()
                               .nlargest(3).index))
   .unstack(level=1).fillna(0).astype(int))

Пример (добавьте еще одну строку 121,2953,1.1 для дополнительного третьего автора):

userID,author_cat,vote
234,1246,5.0
121,2954,3.2
234,1246,2.1
121,2954,1.4
234,1578,3.3
234,1246,4.5
121,2954,1.1
121,9341,3.2
121,2953,1.1

Вывод:

+--------+------+------+------+
|        |  0   |  1   |  2   |
+--------+------+------+------+
| UserID |      |      |      |
+--------+------+------+------+
| 121    | 2954 | 9341 | 2953 |
| 234    | 1246 | 1578 |    0 |
+--------+------+------+------+
0 голосов
/ 06 мая 2019

Раствор без использования apply. Используя groupby.unique, вы получите список уникальных author_cat по порядку его появления. Unnesting это к датафрейму. Назначьте 3 столбца со значениями 0 до df. set_index на df до userID. Наконец, update df из кадра данных groupby.unique:

d1 = df.groupby('userID')['author_cat'].unique()
d2 = pd.DataFrame(d1.tolist(), index=d1.index)

d2
Out[455]:
           0     1
userID
121     2954  9341
234     1246  1578

Заключительные шаги:

df[0], df[1], df[2] = 0, 0, 0
df.set_index('userID', inplace=True)
df.update(d2)

Out[456]:
        author_cat  vote       0       1  2
userID
234           1246   5.0  1246.0  1578.0  0
121           2954   3.2  2954.0  9341.0  0
234           1246   2.1  1246.0  1578.0  0
121           2954   1.4  2954.0  9341.0  0
234           1578   3.3  1246.0  1578.0  0
234           1246   4.5  1246.0  1578.0  0
121           2954   1.1  2954.0  9341.0  0
121           9341   3.2  2954.0  9341.0  0

В вашем образце нет значений третьего столбца, поэтому столбец 2 по-прежнему 0 после update.

Если ваши данные имеют значения третьего столбца, выходные данные будут такими (Примечание: в этом случае я добавил одну строку к вашему образцу для демонстрации третьего столбца ):

Out[462]:
        author_cat    vote     0     1       2
userID
234           1246     5.0  1246  1578     0.0
121           2954     3.2  2954  9341  9954.0
234           1246     2.1  1246  1578     0.0
121           2954     1.4  2954  9341  9954.0
234           1578     3.3  1246  1578     0.0
234           1246     4.5  1246  1578     0.0
121           2954     1.1  2954  9341  9954.0
121           9341     3.2  2954  9341  9954.0
121           9954  9954.0  2954  9341  9954.0
0 голосов
/ 06 мая 2019

Что-то вроде следующего должно работать:

preferences = (
    df
    .groupby(['userID', 'author_cat'])
    .size()
    .rename('count')
    .pipe(lambda x: pd.DataFrame(x))
    .sort_values('count', ascending=False)
    .groupby('userID')
    .apply(lambda x: x.assign(rank=np.arange(len(x.index)) + 1)) # Adds incrementing rank even if counts are equal
    .reset_index()
    .set_index(['userID', 'rank'])
    ['author_cat']
    .unstack(1) # This "pivots" the dataframe
    .filter(lambda x: x <= 3, axis=1)
    .rename(columns={1: 'first_author', 2: 'second_author', 3: 'third_author'})
    .fillna(0)
    .astype('int')
)

df = df.join(preferences, on='userID')

Я предпочитаю groupby().size() вместо value_counts(), он дает аналогичные результаты, но сохраняет имена уровня индекса.

Также, пожалуйста, обратите внимание, что эта реализация не обращает внимания на порядок author_cat, если они имеют одинаковое количество появлений для одинаковых userID.

0 голосов
/ 06 мая 2019

Я не уверен в назначении значений для каждого столбца. Но вы можете вернуть трех лучших авторов для каждого пользователя, используя nlargest с groupby:

df.groupby('userID').author_cat.value_counts().groupby('userID').nlargest(3)

userID  author_cat
121     2954          3
        9341          1
234     1246          3
        1578          1
Name: author_cat, dtype: int64

Учитывая ваш пример df, он выглядит так же, как исходное значение, но если вы запустите его для полного набора данных, вы получите только 3 верхних значения для каждого идентификатора пользователя.

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