如何修复熊猫数据集中缺失的id顺序?



我正试图修复这个数据集的一个问题。链接在这里。因此,我以这种方式加载数据集。

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。对于movie id也是如此。电影id的个数超过实际id的个数。

我只希望它匹配缺失的user_id与现有的user_id,不超过69878。例如,号码75167将成为唯一用户id的最后一个数字,即69878和电影id65133将变成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

对熊猫有什么办法吗?

方法如下:

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值<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中的user_id列)连接。这将在名为new_user_id的列中创建一个df,其中包含所需的新范式user_id值。剩下的就是删除旧范式的user_id列,并将new_user_id重命名为user_id,这就是对droprename的调用所做的。

将movie_id值更改为新范式的逻辑(即,消除唯一值集中的空白)是完全类似的。当我们完成后,我们有这样的输出:

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的唯一值,而无需计数重复或维护其他列(这是groupby在上面的原始解决方案中所做的)
  • 在名为new_user_id
  • 的列中创建一个包含这些唯一值的新数据框
  • 调用reset_index获取索引,该索引是一个非间隙整数范围(一个整数为每个唯一的user_id)
  • 调用set_index,它将创建一个名为'index'的列,其中包含前面的索引(0..)并使user_id作为新的索引
  • 将"index"列重命名为new_user_id
  • 访问new_user_id列,并添加1将id值从0-offset转换为1-offset。

2行:

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

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

最新更新