pandas 嵌套 iterrows 的向量化解决方案
Vectorized solution for pandas nested iterrows
给定一个示例数据框:
example_df = pd.DataFrame({"app_id": [1,2,3,4,5,6] ,
"payment_date":["2021-01-01", "2021-02-01", "2020-03-02", "2020-04-05", "2020-01-05","2020-01-04"],
"user_id": [12,12,12,13,13,13],
"application_date":["2021-02-01", "2021-02-01", "2020-03-02", "2020-04-05", "2020-01-05", "2020-01-04"] , "flag": [1,0,0,1,0,1], "order_column": [1,2,3,4,5, 6]})
应该做的是:
- 我会用一个例子来解释我想做什么:
- 遍历所有行
- 如果标志列等于 1,请执行以下操作
- 第一行
flag
列为 1,该行的 user_id
为 12。查看所有 user_id
= 12 的实例并比较它们的 application_date
第一行的payment_date
。我们看到第二行的 application_date
大于第一行的 payment_date
。那么第一行的label就是1,第三行也属于user_id
=12但是它的application_date
不大于第一行的payment_date。如果第一行的 application_date
大于 payment_date
的一个或多个观察值,则第一行的总标签为 1。如果没有此类观察值,则总标签为 0.
我为此使用 iterrows 编写了代码,但我想要一个更紧凑的矢量化解决方案,因为 iterrows 对于较大的数据集可能会很慢。喜欢
example_df.groupby("something").filter(lambda row: row. ...)
我的代码是:
labels_dict = {}
for idx, row in example_df.iterrows():
if row.flag == 1:
app_id = row.app_id
user_id = row.user_id
user_df = example_df[example_df.user_id == user_id]
labelss = []
for idx2, row2 in user_df.iterrows():
if (row2.order_column != row.order_column) & (row.payment_date < row2.application_date):
label = 1
labelss.append(label)
elif (row2.order_column != row.order_column) & (row.payment_date >= row2.application_date):
label = 0
labelss.append(label)
labels_dict[app_id] = labelss
final_labels = {}
for key, value in labels_dict.items():
if 1 in value:
final_labels[key] = 1
else:
final_labels[key] = 0
final_labels
是预期的输出。基本上,根据我解释的标准,我要求所有 flag
= 1 的行都标记为 1 或 0。
期望的输出:
{1: 1, 4: 0, 6: 1}
Here keys are app_id and values are labels (either 0 or 1)
(i) 将所有日期转换为日期时间对象
(ii) groupby
"user_id" 并为每个组找到第一个 "payment_date" 使用 first
并将其转换为整个 DataFrame。然后使用 lt
(小于)将其与“application_date”进行比较。
(iii) groupby
"user_id" 再次查找满足条件的条目有多少,根据总和是否大于1赋值。
example_df['payment_date'] = pd.to_datetime(example_df['payment_date'])
example_df['application_date'] = pd.to_datetime(example_df['application_date'])
example_df['flag_cumsum'] = example_df['flag'].cumsum()
example_df['first_payment_date < application_date'] = (example_df
.groupby(['flag_cumsum','user_id'])['payment_date']
.transform('first')
.lt(example_df['application_date']))
out = (example_df.groupby('flag_cumsum').agg({'app_id':'first',
'first_payment_date < application_date':'sum'})
.set_index('app_id')['first_payment_date < application_date']
.gt(0).astype(int)
.to_dict())
输出:
{1: 1, 4: 0}
我首先构建一个临时数据帧,其中只有 flag
中的行有 1,然后将其与 user_id
上的完整数据帧合并。
然后我将添加一个新的布尔列,如果 application_date 大于 payment_date 并且如果原始 app_id 与 temp 上的不同(即不同的行)
最后,计算每个 app_id 的真值数量就足够了,如果数量大于 0,则给出 1。
Pandas 代码可以是:
tmp = example_df.loc[example_df['flag'] == 1,
['app_id', 'user_id', 'payment_date']]
tmp = tmp.merge(example_df.drop(columns = 'payment_date'), on='user_id')
tmp['k'] = ((tmp['app_id_x'] != tmp['app_id_y'])
& (tmp['application_date'] > tmp['payment_date']))
d = (tmp.groupby('app_id_x')['k'].sum() != 0).astype('int').to_dict()
根据您的数据,它给出了预期的结果:
{1: 1, 4: 0, 6: 1}
给定一个示例数据框:
example_df = pd.DataFrame({"app_id": [1,2,3,4,5,6] ,
"payment_date":["2021-01-01", "2021-02-01", "2020-03-02", "2020-04-05", "2020-01-05","2020-01-04"],
"user_id": [12,12,12,13,13,13],
"application_date":["2021-02-01", "2021-02-01", "2020-03-02", "2020-04-05", "2020-01-05", "2020-01-04"] , "flag": [1,0,0,1,0,1], "order_column": [1,2,3,4,5, 6]})
应该做的是:
- 我会用一个例子来解释我想做什么:
- 遍历所有行
- 如果标志列等于 1,请执行以下操作
- 第一行
flag
列为 1,该行的user_id
为 12。查看所有user_id
= 12 的实例并比较它们的application_date
第一行的payment_date
。我们看到第二行的application_date
大于第一行的payment_date
。那么第一行的label就是1,第三行也属于user_id
=12但是它的application_date
不大于第一行的payment_date。如果第一行的application_date
大于payment_date
的一个或多个观察值,则第一行的总标签为 1。如果没有此类观察值,则总标签为 0.
我为此使用 iterrows 编写了代码,但我想要一个更紧凑的矢量化解决方案,因为 iterrows 对于较大的数据集可能会很慢。喜欢
example_df.groupby("something").filter(lambda row: row. ...)
我的代码是:
labels_dict = {}
for idx, row in example_df.iterrows():
if row.flag == 1:
app_id = row.app_id
user_id = row.user_id
user_df = example_df[example_df.user_id == user_id]
labelss = []
for idx2, row2 in user_df.iterrows():
if (row2.order_column != row.order_column) & (row.payment_date < row2.application_date):
label = 1
labelss.append(label)
elif (row2.order_column != row.order_column) & (row.payment_date >= row2.application_date):
label = 0
labelss.append(label)
labels_dict[app_id] = labelss
final_labels = {}
for key, value in labels_dict.items():
if 1 in value:
final_labels[key] = 1
else:
final_labels[key] = 0
final_labels
是预期的输出。基本上,根据我解释的标准,我要求所有 flag
= 1 的行都标记为 1 或 0。
期望的输出:
{1: 1, 4: 0, 6: 1}
Here keys are app_id and values are labels (either 0 or 1)
(i) 将所有日期转换为日期时间对象
(ii) groupby
"user_id" 并为每个组找到第一个 "payment_date" 使用 first
并将其转换为整个 DataFrame。然后使用 lt
(小于)将其与“application_date”进行比较。
(iii) groupby
"user_id" 再次查找满足条件的条目有多少,根据总和是否大于1赋值。
example_df['payment_date'] = pd.to_datetime(example_df['payment_date'])
example_df['application_date'] = pd.to_datetime(example_df['application_date'])
example_df['flag_cumsum'] = example_df['flag'].cumsum()
example_df['first_payment_date < application_date'] = (example_df
.groupby(['flag_cumsum','user_id'])['payment_date']
.transform('first')
.lt(example_df['application_date']))
out = (example_df.groupby('flag_cumsum').agg({'app_id':'first',
'first_payment_date < application_date':'sum'})
.set_index('app_id')['first_payment_date < application_date']
.gt(0).astype(int)
.to_dict())
输出:
{1: 1, 4: 0}
我首先构建一个临时数据帧,其中只有 flag
中的行有 1,然后将其与 user_id
上的完整数据帧合并。
然后我将添加一个新的布尔列,如果 application_date 大于 payment_date 并且如果原始 app_id 与 temp 上的不同(即不同的行)
最后,计算每个 app_id 的真值数量就足够了,如果数量大于 0,则给出 1。
Pandas 代码可以是:
tmp = example_df.loc[example_df['flag'] == 1,
['app_id', 'user_id', 'payment_date']]
tmp = tmp.merge(example_df.drop(columns = 'payment_date'), on='user_id')
tmp['k'] = ((tmp['app_id_x'] != tmp['app_id_y'])
& (tmp['application_date'] > tmp['payment_date']))
d = (tmp.groupby('app_id_x')['k'].sum() != 0).astype('int').to_dict()
根据您的数据,它给出了预期的结果:
{1: 1, 4: 0, 6: 1}