python - 如何修复 pandas 数据集中缺少的 id 顺序?

我正在尝试解决此数据集的一个问题。链接是https://grouplens.org/datasets/movielens/10m/。所以,我以这种方式加载了数据集。

df = pd.read_csv('ratings.csv', sep='::', names=['user_id', 'movie_id', 'rating', 'timestamp'])
num_of_unique_users = len(df['user_id'].unique())

唯一用户数为 69878。如果我们打印出数据集的最后一行。我们可以看到用户 id 在 69878 以上。在这种情况下缺少用户 id。电影ID的情况相同。电影 ID 数量超过实际 ID。

我只希望它将丢失的 user_id 与现有的匹配并且不超过 69878。例如,数字 75167 将成为唯一用户 ID 的最后一个数字,即 69878,电影 ID 65133 将成为 10677 最后唯一的电影 ID .

实际的

user_id movie_id    rating  timestamp
0   1   122 5.0 838985046
1   1   185 5.0 838983525
2   1   231 5.0 838983392
3   1   292 5.0 838983421
4   1   316 5.0 838983392
... ... ... ... ...
10000044    71567   1984    1.0 912580553
10000045    71567   1985    1.0 912580553
10000046    71567   1986    1.0 912580553
10000047    71567   2012    3.0 912580722
10000048    71567   2028    5.0 912580344

期望的

user_id movie_id    rating  timestamp
0   1   122 5.0 838985046
1   1   185 5.0 838983525
2   1   231 5.0 838983392
3   1   292 5.0 838983421
4   1   316 5.0 838983392
... ... ... ... ...
10000044    69878   1984    1.0 912580553
10000045    69878   1985    1.0 912580553
10000046    69878   1986    1.0 912580553
10000047    69878   2012    3.0 912580722
10000048    69878   2028    5.0 912580344

有没有办法用 pandas 做到这一点?

回答1

这是一种方法:

df2 = df.groupby('user_id').count().reset_index()
df2 = df2.assign(new_user_id=df2.index + 1).set_index('user_id')
df = df.join(df2['new_user_id'], on='user_id').drop(columns=['user_id']).rename(columns={'new_user_id':'user_id'})

df2 = df.groupby('movie_id').count().reset_index()
df2 = df2.assign(new_movie_id=df2.index + 1).set_index('movie_id')
df = df.join(df2['new_movie_id'], on='movie_id').drop(columns=['movie_id']).rename(columns={'new_movie_id':'movie_id'})

df = pd.concat([df[['user_id', 'movie_id']], df.drop(columns=['user_id', 'movie_id'])], axis=1)

样本输入:

user_id  movie_id  rating  timestamp
0        1         2     5.0  838985046
1        1         4     5.0  838983525
2        3         4     5.0  838983392
3        3         6     5.0  912580553
4        5         2     5.0  912580722
5        5         6     5.0  912580344

样本输出:

user_id  movie_id  rating  timestamp
0        1         1     5.0  838985046
1        1         2     5.0  838983525
2        2         2     5.0  838983392
3        2         3     5.0  912580553
4        3         1     5.0  912580722
5        3         3     5.0  912580344

这是中间结果和解释。

首先我们这样做:

df2 = df.groupby('user_id').count().reset_index()

输出:

user_id  movie_id  rating  timestamp
0        1         2       2          2
1        3         2       2          2
2        5         2       2          2

我们上面所做的是使用 groupby 为每个唯一的 user_id 获取一行。我们调用 count 只是为了将输出(一个 groupby 对象)转换回一个数据帧。我们调用 reset_index 来创建一个没有间隙的新整数范围索引。 (注意:我们关心以供将来使用的唯一列是 user_id。)

接下来我们这样做:

df2 = df2.assign(new_user_id=df2.index + 1).set_index('user_id')

输出:

movie_id  rating  timestamp  new_user_id
user_id
1               2       2          2            1
3               2       2          2            2
5               2       2          2            3

assign 调用创建了一个名为 new_user_id 的新列,我们使用 0 偏移索引加 1 填充该列(这样我们就不会有 id values < 1)。 set_index 调用将我们的索引替换为 user_id,以期将此数据帧的索引用作对 join 的延迟调用的目标。

下一步是:

df = df.join(df2['new_user_id'], on='user_id').drop(columns=['user_id']).rename(columns={'new_user_id':'user_id'})

输出:

movie_id  rating  timestamp  user_id
0         2     5.0  838985046        1
1         4     5.0  838983525        1
2         4     5.0  838983392        2
3         6     5.0  912580553        2
4         2     5.0  912580722        3
5         6     5.0  912580344        3

这里我们只取 df2 的 new_user_id 列并在 df 对象上调用 join,指示方法使用 df 中的 user_id 列(on 参数)与索引(最初是df2)。这将在名为 new_user_id 的列中创建一个具有所需新范式 user_id values 的 df。剩下的就是删除旧范式 user_id 列并将 new_user_id 重命名为 user_id,这就是对 droprename 的调用所做的。

将movie_id values 更改为新范例的逻辑(即消除唯一value 集合中的间隙)是完全类似的。完成后,我们有以下输出:

rating  timestamp  user_id  movie_id
0     5.0  838985046        1         1
1     5.0  838983525        1         2
2     5.0  838983392        2         2
3     5.0  912580553        2         3
4     5.0  912580722        3         1
5     5.0  912580344        3         3

最后,我们使用以下代码对列进行重新排序,使其看起来与原始列相同:

df = pd.concat([df[['user_id', 'movie_id']], df.drop(columns=['user_id', 'movie_id'])], axis=1)

输出:

user_id  movie_id  rating  timestamp
0        1         1     5.0  838985046
1        1         2     5.0  838983525
2        2         2     5.0  838983392
3        2         3     5.0  912580553
4        3         1     5.0  912580722
5        3         3     5.0  912580344

更新:这是一个替代解决方案,它使用 Series.unique() 而不是 gropuby 并节省了几行:

df2 = pd.DataFrame(df.user_id.unique(), columns=['user_id']
    ).reset_index().set_index('user_id').rename(columns={'index':'new_user_id'})['new_user_id'] + 1
df = df.join(df2, on='user_id').drop(columns=['user_id']).rename(columns={'new_user_id':'user_id'})

df2 = pd.DataFrame(df.movie_id.unique(), columns=['movie_id']
    ).reset_index().set_index('movie_id').rename(columns={'index':'new_movie_id'})['new_movie_id'] + 1
df = df.join(df2, on='movie_id'
    ).drop(columns=['movie_id']).rename(columns={'new_movie_id':'movie_id'})

df = pd.concat([df[['user_id', 'movie_id']], df.drop(columns=['user_id', 'movie_id'])], axis=1)

这里的想法是:

第 1 行:

  • 使用 unique 获取 user_id 的唯一 values ,而无需计算重复或维护其他列(这是 groupby 在上面的原始解决方案中所做的)
  • 在名为 new_user_id 的列中创建一个包含这些唯一 values 的新数据框
  • 调用 reset_index 以获取一个无间隙整数范围的索引(每个唯一 user_id 一个整数)
  • 调用 set_index 它将创建一个名为 'index' 的列,其中包含先前的索引(0..number of unique user_id values)并使 user_id 成为新索引
  • 将标有“索引”的列重命名为 new_user_id
  • 访问 new_user_id 列并添加 1 以从 0-offset 转换为 1-offset id value。

第 2 行:

  • 就像我们在原始解决方案中所做的那样调用 join,除了 other 数据框只是 df2(这很好,因为它只有一个列,new_user_id)。

movie_id 的逻辑完全类似,使用 concat 的最后一行与上面的原始解决方案相同。

相似文章

随机推荐

最新文章