解决方案要点:
(答案末尾有完整代码)
# reshape
dfm = pd.melt(df, id_vars=['UserID'], value_vars=df.columns[1:])
# rename
dfm.columns = ['UserID', 'variable', 'hour']
# crosstab into final form
df_ct = pd.crosstab(dfm['UserID'],dfm['hour'],dfm['variable'],aggfunc='last')
输出
hour 1 2 3 4
UserID
24 Page1 Page3 NaN NaN
35 Page3 NaN NaN NaN
39 NaN Page1 Page2 Page3
一些细节
我最近遇到了类似的挑战,并且在How to handle ValueError: Index contains duplicate entries using df.pivot or pd.pivot_table? 上也得到了一些很好的建议,这些建议在这里也应该有用。在你的特殊情况下,我会这样处理:
- 通过
df.cumsum()获取累计时间,
- 使用
df / (60*60) 将秒转换为小时,
- 使用带有
df.astype(int) 的整数关注完整小时数,
- 重塑您的
df,以便使用 pd.melt() 进行更轻松的计算,然后
- 通过
pd.crosstab()获得最终形式
结果一:
hour 0 1 2 3
UserID
24 Page1 Page3 NaN NaN
35 Page3 NaN NaN NaN
39 NaN Page1 Page2 Page3
然后由您决定如何呈现信息。按照现在的情况,User 24 完成了 Page1 before an hour had passed,并在第二个小时开始之前完成了 Page2 and Page3。在1 开始工作可能感觉更自然。在这种情况下,只需包含
df = df + 1
在初始步骤中得到:
结果 2:时间从1 开始
hour 1 2 3 4
UserID
24 Page1 Page3 NaN NaN
35 Page3 NaN NaN NaN
39 NaN Page1 Page2 Page3
现在这只是一个问题,如何处理NaNs。您可以将.fill() 包含在:
d.crosstab(dfm['UserID'],dfm['hour'],dfm['variable'],aggfunc='last')#.ffill(axis=1)
得到:
结果3:照顾NaNs
hour 1 2 3 4
UserID
24 Page1 Page3 Page3 Page3
35 Page3 Page3 Page3 Page3
39 NaN Page1 Page2 Page3
User 39 仍然有一个 NaN 用于 'Hour1,但我会等待您对您想用它做什么的反馈。
完整代码:
# imports
import pandas as pd
# data
df_i = pd.DataFrame({'UserID': {0: 24, 1: 39, 2: 35},
'Page1': {0: 2789, 1: 3745, 2: 100},
'Page2': {0: 2375, 1: 3800, 2: 300},
'Page3': {0: 574, 1: 4567, 2: 1005}}).set_index('UserID')
# time conversion
df = df_i.cumsum(axis = 1)
df = df / (60*60)
df = df.astype(int)
df = df + 1
df = df.reset_index()
# reshape
dfm = pd.melt(df, id_vars=['UserID'], value_vars=df.columns[1:])
# rename
dfm.columns = ['UserID', 'variable', 'hour']
# crosstab into final form
df_ct = pd.crosstab(dfm['UserID'],dfm['hour'],dfm['variable'],aggfunc='last').ffill(axis=1)
df_ct
# df