使用 pandas 将带有填充零的序列号附加到系列
append sequence number with padded zeroes to a series using padas
我有一个如下所示的数据框
df = pd.DataFrame({'person_id': [101,101,101,101,202,202,202],
'login_date':['5/7/2013 09:27:00 AM','09/08/2013 11:21:00 AM','06/06/2014 08:00:00 AM','06/06/2014 05:00:00 AM','12/11/2011 10:00:00 AM','13/10/2012 12:00:00 AM','13/12/2012 11:45:00 AM']})
df.login_date = pd.to_datetime(df.login_date)
df['logout_date'] = df.login_date + pd.Timedelta(days=5)
df['login_id'] = [1,1,1,1,8,8,8]
正如您在示例数据框中看到的那样,login_id
是相同的,即使 login
和 logout
日期对于这个人来说是不同的。
例如,person = 101
,已在 4 个不同的时间戳登录和注销。但他得到了相同的 login_ids 这是不正确的。
相反,我想生成一个 new login_id
列,每个人在其中获得一个新的 login_id,但在他们随后的登录中保留 1st login_id
信息。所以,我们可以知道它是一个序列
我尝试了以下方法,但效果不佳
df.groupby(['person_id','login_date','logout_date'])['login_id'].rank(method="first", ascending=True) + 100000
我希望我的输出如下所示。你可以看到 1
和 8
,每个人的第一个 login_id 如何保留在他们随后的 login_ids
中。我们只是通过添加 00001
并根据行数加一来添加一个序列。
请注意,我想将其应用于大数据,login_ids
可能不仅仅是真实数据中的 single digit
。例如,第一个 login_id 甚至可以是 576869578
等随机数。在这种情况下,后续登录 ID 将为 57686957800001
。希望这可以帮助。无论该主题的第一个 login_id
是什么,根据该人拥有的行数添加 00001
、00002
等。希望这有帮助
login_id = df.groupby('person_id').login_id.apply(list)
def modify_id(x):
result= []
for index,value in enumerate(x):
if index > 0:
value = (int(value) * 100000) + index
result.append(value)
return result
df['ogin_id'] = login_id.apply(lambda x : modify_id(x)).explode().to_list()
将给出输出-
person_id
login_date
logout_date
login_id
101
2013-05-07 09:27:00
2013-05-12 09:27:00
1
101
2013-09-08 11:21:00
2013-09-13 11:21:00
100001
101
2014-06-06 08:00:00
2014-06-11 08:00:00
100002
101
2014-06-06 05:00:00
2014-06-11 05:00:00
100003
202
2011-12-11 10:00:00
2011-12-16 10:00:00
8
202
2012-10-13 00:00:00
2012-10-18 00:00:00
800001
202
2012-12-13 11:45:00
2012-12-18 11:45:00
800002
更新2: 刚刚意识到我之前的回答也给第一个索引添加了100000。这是一个使用 GroupBy.transform()
仅向后续索引添加 100000 的版本:
cumcount = df.groupby(['person_id','login_id']).login_id.cumcount()
df.login_id = df.groupby(['person_id','login_id']).login_id.transform(
lambda x: x.shift().mul(100000).fillna(x.min())
).add(cumcount)
person_id login_date logout_date login_id
# 0 101 2013-05-07 09:27:00 2013-05-12 09:27:00 1
# 1 101 2013-09-08 11:21:00 2013-09-13 11:21:00 100001
# 2 101 2014-06-06 08:00:00 2014-06-11 08:00:00 100002
# 3 101 2014-06-06 05:00:00 2014-06-11 05:00:00 100003
# 4 202 2011-12-11 10:00:00 2011-12-16 10:00:00 8
# 5 202 2012-10-13 00:00:00 2012-10-18 00:00:00 800001
# 6 202 2012-12-13 11:45:00 2012-12-18 11:45:00 800002
更新: 更快的选择是使用 GroupBy.cumcount()
构建序列:
cumcount = df.groupby(['person_id','login_id']).login_id.cumcount()
df.login_id = df.login_id.mul(100000).add(cumcount)
# person_id login_date logout_date login_id
# 0 101 2013-05-07 09:27:00 2013-05-12 09:27:00 100000
# 1 101 2013-09-08 11:21:00 2013-09-13 11:21:00 100001
# 2 101 2014-06-06 08:00:00 2014-06-11 08:00:00 100002
# 3 101 2014-06-06 05:00:00 2014-06-11 05:00:00 100003
# 4 202 2011-12-11 10:00:00 2011-12-16 10:00:00 800000
# 5 202 2012-10-13 00:00:00 2012-10-18 00:00:00 800001
# 6 202 2012-12-13 11:45:00 2012-12-18 11:45:00 800002
您可以在 GroupBy.apply()
:
中构建序列
df.login_id = df.groupby(['person_id','login_id']).login_id.apply(
lambda x: pd.Series([x.min()*100000+seq for seq in range(len(x))], x.index)
)
你可以利用你原来的rank()
df['login_id'] = df['login_id'] * 100000 + df.groupby(['person_id'])['login_id'].rank(method="first") - 1
# print(df)
person_id login_date logout_date login_id
0 101 2013-05-07 09:27:00 2013-05-12 09:27:00 100000.0
1 101 2013-09-08 11:21:00 2013-09-13 11:21:00 100001.0
2 101 2014-06-06 08:00:00 2014-06-11 08:00:00 100002.0
3 101 2014-06-06 05:00:00 2014-06-11 05:00:00 100003.0
4 202 2011-12-11 10:00:00 2011-12-16 10:00:00 800000.0
5 202 2012-10-13 00:00:00 2012-10-18 00:00:00 800001.0
6 202 2012-12-13 11:45:00 2012-12-18 11:45:00 800002.0
然后改变了每组的第一行
def change_first(group):
group.loc[group.index[0], 'login_id'] = group.iloc[0]['login_id'] / 100000
return group
df['login_id'] = df.groupby(['person_id']).apply(lambda group: change_first(group))['login_id']
# print(df)
person_id login_date logout_date login_id
0 101 2013-05-07 09:27:00 2013-05-12 09:27:00 1.0
1 101 2013-09-08 11:21:00 2013-09-13 11:21:00 100001.0
2 101 2014-06-06 08:00:00 2014-06-11 08:00:00 100002.0
3 101 2014-06-06 05:00:00 2014-06-11 05:00:00 100003.0
4 202 2011-12-11 10:00:00 2011-12-16 10:00:00 8.0
5 202 2012-10-13 00:00:00 2012-10-18 00:00:00 800001.0
6 202 2012-12-13 11:45:00 2012-12-18 11:45:00 800002.0
或者利用where()
只更新条件为False的行。
df_ = df['login_id'] * 100000 + df.groupby(['person_id'])['login_id'].rank(method="first") - 1
firsts = df.groupby(['person_id']).head(1).index
df['login_id'] = df['login_id'].where(df.index.isin(firsts), df_)
我有一个如下所示的数据框
df = pd.DataFrame({'person_id': [101,101,101,101,202,202,202],
'login_date':['5/7/2013 09:27:00 AM','09/08/2013 11:21:00 AM','06/06/2014 08:00:00 AM','06/06/2014 05:00:00 AM','12/11/2011 10:00:00 AM','13/10/2012 12:00:00 AM','13/12/2012 11:45:00 AM']})
df.login_date = pd.to_datetime(df.login_date)
df['logout_date'] = df.login_date + pd.Timedelta(days=5)
df['login_id'] = [1,1,1,1,8,8,8]
正如您在示例数据框中看到的那样,login_id
是相同的,即使 login
和 logout
日期对于这个人来说是不同的。
例如,person = 101
,已在 4 个不同的时间戳登录和注销。但他得到了相同的 login_ids 这是不正确的。
相反,我想生成一个 new login_id
列,每个人在其中获得一个新的 login_id,但在他们随后的登录中保留 1st login_id
信息。所以,我们可以知道它是一个序列
我尝试了以下方法,但效果不佳
df.groupby(['person_id','login_date','logout_date'])['login_id'].rank(method="first", ascending=True) + 100000
我希望我的输出如下所示。你可以看到 1
和 8
,每个人的第一个 login_id 如何保留在他们随后的 login_ids
中。我们只是通过添加 00001
并根据行数加一来添加一个序列。
请注意,我想将其应用于大数据,login_ids
可能不仅仅是真实数据中的 single digit
。例如,第一个 login_id 甚至可以是 576869578
等随机数。在这种情况下,后续登录 ID 将为 57686957800001
。希望这可以帮助。无论该主题的第一个 login_id
是什么,根据该人拥有的行数添加 00001
、00002
等。希望这有帮助
login_id = df.groupby('person_id').login_id.apply(list)
def modify_id(x):
result= []
for index,value in enumerate(x):
if index > 0:
value = (int(value) * 100000) + index
result.append(value)
return result
df['ogin_id'] = login_id.apply(lambda x : modify_id(x)).explode().to_list()
将给出输出-
person_id | login_date | logout_date | login_id |
---|---|---|---|
101 | 2013-05-07 09:27:00 | 2013-05-12 09:27:00 | 1 |
101 | 2013-09-08 11:21:00 | 2013-09-13 11:21:00 | 100001 |
101 | 2014-06-06 08:00:00 | 2014-06-11 08:00:00 | 100002 |
101 | 2014-06-06 05:00:00 | 2014-06-11 05:00:00 | 100003 |
202 | 2011-12-11 10:00:00 | 2011-12-16 10:00:00 | 8 |
202 | 2012-10-13 00:00:00 | 2012-10-18 00:00:00 | 800001 |
202 | 2012-12-13 11:45:00 | 2012-12-18 11:45:00 | 800002 |
更新2: 刚刚意识到我之前的回答也给第一个索引添加了100000。这是一个使用 GroupBy.transform()
仅向后续索引添加 100000 的版本:
cumcount = df.groupby(['person_id','login_id']).login_id.cumcount()
df.login_id = df.groupby(['person_id','login_id']).login_id.transform(
lambda x: x.shift().mul(100000).fillna(x.min())
).add(cumcount)
person_id login_date logout_date login_id
# 0 101 2013-05-07 09:27:00 2013-05-12 09:27:00 1
# 1 101 2013-09-08 11:21:00 2013-09-13 11:21:00 100001
# 2 101 2014-06-06 08:00:00 2014-06-11 08:00:00 100002
# 3 101 2014-06-06 05:00:00 2014-06-11 05:00:00 100003
# 4 202 2011-12-11 10:00:00 2011-12-16 10:00:00 8
# 5 202 2012-10-13 00:00:00 2012-10-18 00:00:00 800001
# 6 202 2012-12-13 11:45:00 2012-12-18 11:45:00 800002
更新: 更快的选择是使用 GroupBy.cumcount()
构建序列:
cumcount = df.groupby(['person_id','login_id']).login_id.cumcount()
df.login_id = df.login_id.mul(100000).add(cumcount)
# person_id login_date logout_date login_id
# 0 101 2013-05-07 09:27:00 2013-05-12 09:27:00 100000
# 1 101 2013-09-08 11:21:00 2013-09-13 11:21:00 100001
# 2 101 2014-06-06 08:00:00 2014-06-11 08:00:00 100002
# 3 101 2014-06-06 05:00:00 2014-06-11 05:00:00 100003
# 4 202 2011-12-11 10:00:00 2011-12-16 10:00:00 800000
# 5 202 2012-10-13 00:00:00 2012-10-18 00:00:00 800001
# 6 202 2012-12-13 11:45:00 2012-12-18 11:45:00 800002
您可以在 GroupBy.apply()
:
df.login_id = df.groupby(['person_id','login_id']).login_id.apply(
lambda x: pd.Series([x.min()*100000+seq for seq in range(len(x))], x.index)
)
你可以利用你原来的rank()
df['login_id'] = df['login_id'] * 100000 + df.groupby(['person_id'])['login_id'].rank(method="first") - 1
# print(df)
person_id login_date logout_date login_id
0 101 2013-05-07 09:27:00 2013-05-12 09:27:00 100000.0
1 101 2013-09-08 11:21:00 2013-09-13 11:21:00 100001.0
2 101 2014-06-06 08:00:00 2014-06-11 08:00:00 100002.0
3 101 2014-06-06 05:00:00 2014-06-11 05:00:00 100003.0
4 202 2011-12-11 10:00:00 2011-12-16 10:00:00 800000.0
5 202 2012-10-13 00:00:00 2012-10-18 00:00:00 800001.0
6 202 2012-12-13 11:45:00 2012-12-18 11:45:00 800002.0
然后改变了每组的第一行
def change_first(group):
group.loc[group.index[0], 'login_id'] = group.iloc[0]['login_id'] / 100000
return group
df['login_id'] = df.groupby(['person_id']).apply(lambda group: change_first(group))['login_id']
# print(df)
person_id login_date logout_date login_id
0 101 2013-05-07 09:27:00 2013-05-12 09:27:00 1.0
1 101 2013-09-08 11:21:00 2013-09-13 11:21:00 100001.0
2 101 2014-06-06 08:00:00 2014-06-11 08:00:00 100002.0
3 101 2014-06-06 05:00:00 2014-06-11 05:00:00 100003.0
4 202 2011-12-11 10:00:00 2011-12-16 10:00:00 8.0
5 202 2012-10-13 00:00:00 2012-10-18 00:00:00 800001.0
6 202 2012-12-13 11:45:00 2012-12-18 11:45:00 800002.0
或者利用where()
只更新条件为False的行。
df_ = df['login_id'] * 100000 + df.groupby(['person_id'])['login_id'].rank(method="first") - 1
firsts = df.groupby(['person_id']).head(1).index
df['login_id'] = df['login_id'].where(df.index.isin(firsts), df_)