用Python的 DataFrame按日期进行滚动计算求和并拼接一个字符串
我需要在一个滚动窗口内对交易金额求和,并把构成总和的交易ID连接起来。
完整代码在底部。
下面的语句返回预期的结果。
rollingSum = df.groupby(
["EMPLOYEE ID", "ACCOUNT EMAIL ADDRESS", 'ACCOUNT NUMBER', 'MERCHANT NAME']
).rolling(window='11D')['TRANSACTION AMOUNT'].sum()
rollingCnt = df.groupby(
["EMPLOYEE ID", "ACCOUNT EMAIL ADDRESS", 'ACCOUNT NUMBER', 'MERCHANT NAME']
).rolling(window='11D')['TRANSACTION ID'].count()
下面的代码中,我想把构成上述和的交易ID连接起来,并对其进行计数。
如下会抛出:TypeError: must be real number, not str
rollingTransId = df.groupby(
["EMPLOYEE ID", "ACCOUNT EMAIL ADDRESS", 'ACCOUNT NUMBER', 'MERCHANT NAME']
).rolling(window='11D')['TRANSACTION ID STR'].apply(
lambda x: ','.join(x.dropna().astype(str)))
# -*- coding: utf-8 -*-
"""
Created on Fri Jun 5 11:26:48 2026
@author: ne098406
"""
import io
import os
import pandas as pd
DATA = """
TRANSACTION DATE,EMPLOYEE ID,ACCOUNT EMAIL ADDRESS,ACCOUNT NUMBER,MERCHANT NAME,EXPENSE DESCRIPTION,TRANSACTION ID,TRANSACTION AMOUNT
2025-12-02,52846,[email protected],846,HARBOR FREIGHT,electrician tool,5454344019001,131.18
2025-12-10,52846,[email protected],456,Lowes,Pipe Insulation and fitting,5459405088001,34.79
2025-12-20,52846,[email protected],846,nino's pizza,lunch,5459405089001,98.98
2025-12-31,52846,[email protected],456,Lowes,nuts and bolts for maintenance,5471834423001,56.47
2026-01-01,52846,[email protected],846,nino's pizza,lunch,5471834424001,139
2026-01-06,52846,[email protected],456,Lowes,Cable Cutter Pliers,5347075493001,83.98
2026-01-27,52846,[email protected],456,local hardware,paint,5347075494001,242.37
2026-01-29,52846,[email protected],456,local hardware,Pipe Insulation and fitting,5357832073001,15.62
2026-02-02,52846,[email protected],846,corner store,OFFICE CLOCK,5361456559001,29.44
2026-02-05,52846,[email protected],846,corner store,office supplies,5386878553001,716.2
2026-02-09,52846,[email protected],846,pepe's pizza,lunch,5401420274001,642.2
2026-02-11,52846,[email protected],456,nino's pizza,lunch,5403179700001,29.15
2026-03-03,52846,[email protected],456,nino's pizza,lunch,5414434974001,105.9
2026-04-10,52846,[email protected],456,nino's pizza,lunch,5414434975001,191.27
2025-12-02,77400,[email protected],707,Duffy's Lumber,Nails,5350647122001,154.7
2025-12-10,77400,[email protected],400,Duffy's Lumber,screws,5353050810001,316.62
2025-12-23,77400,[email protected],707,Duffy's Lumber,Fittings for compressor,5356156974001,170.02
2025-12-31,77400,[email protected],707,Duffy's Lumber,fuses,5359620179001,62.84
2026-01-03,77400,[email protected],707,Duffy's Lumber,Nails,5372912596001,366.8
2026-01-06,77400,[email protected],400,HARBOR FREIGHT,bolts,5390241877001,748
2026-01-27,77400,[email protected],400,HARBOR FREIGHT,Cable Cutter Pliers,5401420255001,200
2026-01-29,77400,[email protected],707,HARBOR FREIGHT,bolts,5409512783001,47.49
2026-02-02,77400,[email protected],707,WB Mason,whiteboard calendar & desk calendar,5423349484001,34.91
2026-02-05,77400,[email protected],400,WB Mason,OFFICE CHAIR,5425031381001,48.99
2026-02-09,77400,[email protected],400,HARBOR FREIGHT,electrician tool,5425031382001,152.9
2026-02-11,77400,[email protected],707,WB Mason,mouse pad,5451761817001,34.03
2026-04-10,77400,[email protected],400,Duffy's Lumber,sump pump,5451761818001,25.73
2026-04-15,77400,[email protected],400,Duffy's Lumber,Insulation pipe and wrap,5473621360001,189.99
2025-12-03,11608,[email protected],116,corner store,spare keys,5403179702001,14.25
2025-12-10,11608,[email protected],608,corner store,coffee donuts,5403179703001,256.9
2025-12-20,11608,[email protected],116,corner store,coffee and donuts for all hands meeting,5403179704001,45.98
2025-12-20,11608,[email protected],116,corner store,Bottled Water,5403179705001,139.98
2025-12-31,11608,[email protected],608,corner store,Batteries,5405062431001,24.12
2026-01-03,11608,[email protected],116,WB Mason,envelopes,5406965833001,67.9
2026-01-05,11608,[email protected],608,Staples,whiteboard calendar & desk calendar,5406965834001,73.93
2026-01-06,11608,[email protected],608,Staples,office supplies,5412746497001,327
2026-01-06,11608,[email protected],608,Staples,office supplies,5416288516001,92.98
2026-01-25,11608,[email protected],116,nino's pizza,lunch for crew,5416288517001,43.75
2026-01-29,11608,[email protected],608,Lowes,electrician tool,5416288518001,54.99
2026-02-02,11608,[email protected],116,nino's pizza,meatball hero,5416288519001,39.4
2026-02-05,11608,[email protected],608,Lowes,nuts and bolts for maintenance,5416288520001,68.03
2026-02-09,11608,[email protected],116,Lowes,Cable Cutter Pliers,5416288521001,75.04
2026-02-11,11608,[email protected],608,nino's pizza,large pie,5416288522001,41.96
2026-03-03,11608,[email protected],116,Duffy's Lumber,tape measure,5416288523001,142.52
2026-02-15,11608,[email protected],608,Lowes,3/4 plywood,5416288524001,59.99
"""
DATE_COL = "TRANSACTION DATE"
DATE_FORMAT = "%Y-%m-%d"
GROUP_COLS = [
"EMPLOYEE ID",
"ACCOUNT EMAIL ADDRESS",
"ACCOUNT NUMBER",
"MERCHANT NAME",
]
df = pd.read_csv(io.StringIO(DATA), parse_dates=[DATE_COL], date_format=DATE_FORMAT)
df['TRANSACTION DATE'] = pd.to_datetime(df['TRANSACTION DATE'])
df.set_index('TRANSACTION DATE', inplace=True)
df.sort_index()
df['ACCOUNT NUMBER'] = df['ACCOUNT NUMBER'].astype('Int64')
df['TRANSACTION ID STR'] = df['TRANSACTION ID'].astype('string')
## ## df = df.sort_values(GROUP_COLS + [DATE_COL])
print(df)
rollingSum = df.groupby(
["EMPLOYEE ID", "ACCOUNT EMAIL ADDRESS", 'ACCOUNT NUMBER', 'MERCHANT NAME']
).rolling(window='11D')['TRANSACTION AMOUNT'].sum()
rollingCnt = df.groupby(
["EMPLOYEE ID", "ACCOUNT EMAIL ADDRESS", 'ACCOUNT NUMBER', 'MERCHANT NAME']
).rolling(window='11D')['TRANSACTION ID'].count()
rollingTransId = df.groupby(
["EMPLOYEE ID", "ACCOUNT EMAIL ADDRESS", 'ACCOUNT NUMBER', 'MERCHANT NAME']
).rolling(window='11D')['TRANSACTION ID STR'].apply(
lambda x: ','.join(x.dropna().astype(str)))
解决方案
在前一个问题的评论中,我给出了答案
pandas - python dataframe rolling by date to get sum - Stack Overflow
似乎 rolling() 只允许 apply() 这样的函数,它只能生成数字,不能生成字符串。
你可以把 rolling 转换为 pandas.Series 并使用 apply()
all_windows = df.groupby(
["EMPLOYEE ID", "ACCOUNT EMAIL ADDRESS", "ACCOUNT NUMBER", "MERCHANT NAME"]
).rolling(window="11D")["TRANSACTION ID STR"]
all_windows = pd.Series(all_windows)
rollingTransId = all_windows.apply(lambda window: ",".join(window.dropna().astype(str)))
这对我来说用你提供的代码可行,我得到的是:
0 5416288523001
1 5416288521001
2 5406965833001
3 5403179702001
4 5403179704001
5 5403179704001,5403179705001
6 5416288517001
7 5416288517001,5416288519001
8 5416288518001
9 5416288518001,5416288520001
10 5416288520001,5416288524001
11 5406965834001
12 5406965834001,5412746497001
13 5406965834001,5412746497001,5416288516001
14 5403179703001
15 5405062431001
16 5416288522001
17 5459405088001
18 5471834423001
19 5471834423001,5347075493001
20 5347075494001
21 5347075494001,5357832073001
22 5403179700001
23 5414434974001
24 5414434975001
25 5454344019001
26 5361456559001
27 5361456559001,5386878553001
28 5459405089001
29 5471834424001
30 5401420274001
31 5353050810001
32 5451761818001
33 5451761818001,5473621360001
34 5390241877001
35 5401420255001
36 5425031382001
37 5425031381001
38 5350647122001
39 5356156974001
40 5356156974001,5359620179001
41 5359620179001,5372912596001
42 5409512783001
43 5423349484001
44 5423349484001,5451761817001
最终你也可以使用普通的 for-循环来运行
all_windows = df.groupby(
["EMPLOYEE ID", "ACCOUNT EMAIL ADDRESS", "ACCOUNT NUMBER", "MERCHANT NAME"]
).rolling(window="11D")["TRANSACTION ID STR"]
rollingTransId = []
for window in all_windows:
text = ",".join(window.dropna().astype(str))
rollingTransId.append(text)
你甚至可以把它简化为列表推导式
all_windows = df.groupby(
["EMPLOYEE ID", "ACCOUNT EMAIL ADDRESS", "ACCOUNT NUMBER", "MERCHANT NAME"]
).rolling(window="11D")["TRANSACTION ID STR"]
rollingTransId = [
",".join(window.dropna().astype(str)) for window in all_windows
]
我尝试用 DataFrame 代替 Series 做同样的事情,但它不起作用——我原本以为在前一个问题中它是可行的。
完整的可运行示例。
我测试了不同的方法:for-loop、列表推导式、Series、DataFrame(这不工作)
import io
import os
import pandas as pd
DATA = """
TRANSACTION DATE,EMPLOYEE ID,ACCOUNT EMAIL ADDRESS,ACCOUNT NUMBER,MERCHANT NAME,EXPENSE DESCRIPTION,TRANSACTION ID,TRANSACTION AMOUNT
2025-12-02,52846,[email protected],846,HARBOR FREIGHT,electrician tool,5454344019001,131.18
2025-12-10,52846,[email protected],456,Lowes,Pipe Insulation and fitting,5459405088001,34.79
2025-12-20,52846,[email protected],846,nino's pizza,lunch,5459405089001,98.98
2025-12-31,52846,[email protected],456,Lowes,nuts and bolts for maintenance,5471834423001,56.47
2026-01-01,52846,[email protected],846,nino's pizza,lunch,5471834424001,139
2026-01-06,52846,[email protected],456,Lowes,Cable Cutter Pliers,5347075493001,83.98
2026-01-27,52846,[email protected],456,local hardware,paint,5347075494001,242.37
2026-01-29,52846,[email protected],456,local hardware,Pipe Insulation and fitting,5357832073001,15.62
2026-02-02,52846,[email protected],846,corner store,OFFICE CLOCK,5361456559001,29.44
2026-02-05,52846,[email protected],846,corner store,office supplies,5386878553001,716.2
2026-02-09,52846,[email protected],846,pepe's pizza,lunch,5401420274001,642.2
2026-02-11,52846,[email protected],456,nino's pizza,lunch,5403179700001,29.15
2026-03-03,52846,[email protected],456,nino's pizza,lunch,5414434974001,105.9
2026-04-10,52846,[email protected],456,nino's pizza,lunch,5414434975001,191.27
2025-12-02,77400,[email protected],707,Duffy's Lumber,Nails,5350647122001,154.7
2025-12-10,77400,[email protected],400,Duffy's Lumber,screws,5353050810001,316.62
2025-12-23,77400,[email protected],707,Duffy's Lumber,Fittings for compressor,5356156974001,170.02
2025-12-31,77400,[email protected],707,Duffy's Lumber,fuses,5359620179001,62.84
2026-01-03,77400,[email protected],707,Duffy's Lumber,Nails,5372912596001,366.8
2026-01-06,77400,[email protected],400,HARBOR FREIGHT,bolts,5390241877001,748
2026-01-27,77400,[email protected],400,HARBOR FREIGHT,Cable Cutter Pliers,5401420255001,200
2026-01-29,77400,[email protected],707,HARBOR FREIGHT,bolts,5409512783001,47.49
2026-02-02,77400,[email protected],707,WB Mason,whiteboard calendar & desk calendar,5423349484001,34.91
2026-02-05,77400,[email protected],400,WB Mason,OFFICE CHAIR,5425031381001,48.99
2026-02-09,77400,[email protected],400,HARBOR FREIGHT,electrician tool,5425031382001,152.9
2026-02-11,77400,[email protected],707,WB Mason,mouse pad,5451761817001,34.03
2026-04-10,77400,[email protected],400,Duffy's Lumber,sump pump,5451761818001,25.73
2026-04-15,77400,[email protected],400,Duffy's Lumber,Insulation pipe and wrap,5473621360001,189.99
2025-12-03,11608,[email protected],116,corner store,spare keys,5403179702001,14.25
2025-12-10,11608,[email protected],608,corner store,coffee donuts,5403179703001,256.9
2025-12-20,11608,[email protected],116,corner store,coffee and donuts for all hands meeting,5403179704001,45.98
2025-12-20,11608,[email protected],116,corner store,Bottled Water,5403179705001,139.98
2025-12-31,11608,[email protected],608,corner store,Batteries,5405062431001,24.12
2026-01-03,11608,[email protected],116,WB Mason,envelopes,5406965833001,67.9
2026-01-05,11608,[email protected],608,Staples,whiteboard calendar & desk calendar,5406965834001,73.93
2026-01-06,11608,[email protected],608,Staples,office supplies,5412746497001,327
2026-01-06,11608,[email protected],608,Staples,office supplies,5416288516001,92.98
2026-01-25,11608,[email protected],116,nino's pizza,lunch for crew,5416288517001,43.75
2026-01-29,11608,[email protected],608,Lowes,electrician tool,5416288518001,54.99
2026-02-02,11608,[email protected],116,nino's pizza,meatball hero,5416288519001,39.4
2026-02-05,11608,[email protected],608,Lowes,nuts and bolts for maintenance,5416288520001,68.03
2026-02-09,11608,[email protected],116,Lowes,Cable Cutter Pliers,5416288521001,75.04
2026-02-11,11608,[email protected],608,nino's pizza,large pie,5416288522001,41.96
2026-03-03,11608,[email protected],116,Duffy's Lumber,tape measure,5416288523001,142.52
2026-02-15,11608,[email protected],608,Lowes,3/4 plywood,5416288524001,59.99
"""
DATE_COL = "TRANSACTION DATE"
DATE_FORMAT = "%Y-%m-%d"
GROUP_COLS = [
"EMPLOYEE ID",
"ACCOUNT EMAIL ADDRESS",
"ACCOUNT NUMBER",
"MERCHANT NAME",
]
df = pd.read_csv(io.StringIO(DATA), parse_dates=[DATE_COL], date_format=DATE_FORMAT)
df["TRANSACTION DATE"] = pd.to_datetime(df["TRANSACTION DATE"])
df.set_index("TRANSACTION DATE", inplace=True)
df.sort_index()
df["ACCOUNT NUMBER"] = df["ACCOUNT NUMBER"].astype("Int64")
df["TRANSACTION ID STR"] = df["TRANSACTION ID"].astype("string")
## ## df = df.sort_values(GROUP_COLS + [DATE_COL])
print("--- df ---")
print(df)
rollingSum = (
df.groupby(
["EMPLOYEE ID", "ACCOUNT EMAIL ADDRESS", "ACCOUNT NUMBER", "MERCHANT NAME"]
)
.rolling(window="11D")["TRANSACTION AMOUNT"]
.sum()
)
rollingCnt = (
df.groupby(
["EMPLOYEE ID", "ACCOUNT EMAIL ADDRESS", "ACCOUNT NUMBER", "MERCHANT NAME"]
)
.rolling(window="11D")["TRANSACTION ID"]
.count()
)
def version_0(df):
print("\n--- original version - doesn't work ---\n")
try:
rollingTransId = (
df.groupby(
[
"EMPLOYEE ID",
"ACCOUNT EMAIL ADDRESS",
"ACCOUNT NUMBER",
"MERCHANT NAME",
]
)
.rolling(window="11D")["TRANSACTION ID STR"]
.apply(lambda x: ",".join(x.dropna().astype(str)))
)
print("--- rollingTransId ---")
print(rollingTransId)
except Exception as ex:
print("Ex:", ex)
def version_1_a(df):
print("\n--- for-loop ---\n")
all_windows = df.groupby(
["EMPLOYEE ID", "ACCOUNT EMAIL ADDRESS", "ACCOUNT NUMBER", "MERCHANT NAME"]
).rolling(window="11D")["TRANSACTION ID STR"]
rollingTransId = []
for window in all_windows:
text = ",".join(window.dropna().astype(str))
rollingTransId.append(text)
print("--- rollingTransId ---")
print(rollingTransId)
def version_1_b(df):
print("\n--- list comprehension ---\n")
all_windows = df.groupby(
["EMPLOYEE ID", "ACCOUNT EMAIL ADDRESS", "ACCOUNT NUMBER", "MERCHANT NAME"]
).rolling(window="11D")["TRANSACTION ID STR"]
rollingTransId = [
",".join(window.dropna().astype(str)) for window in all_windows
]
print("--- rollingTransId ---")
print(rollingTransId)
def version_2(df):
print("\n--- Series ---\n")
all_windows = df.groupby(
["EMPLOYEE ID", "ACCOUNT EMAIL ADDRESS", "ACCOUNT NUMBER", "MERCHANT NAME"]
).rolling(window="11D")["TRANSACTION ID STR"]
all_windows = pd.Series(all_windows)
rollingTransId = all_windows.apply(
lambda window: ",".join(window.dropna().astype(str))
)
# rollingTransId = pd.Series(all_windows).apply(
# lambda window: ",".join(window.dropna().astype(str))
# )
print("--- rollingTransId ---")
print(rollingTransId.to_list())
def version_3(df):
print("\n--- DataFrame ---\n")
all_windows = df.groupby(
["EMPLOYEE ID", "ACCOUNT EMAIL ADDRESS", "ACCOUNT NUMBER", "MERCHANT NAME"]
).rolling(window="11D")["TRANSACTION ID STR"]
# print(f"{type(all_windows)=}")
all_windows = pd.DataFrame(all_windows)
rollingTransId = all_windows.apply(
lambda window: ",".join(window.dropna().astype(str))
)
print("--- rollingTransId ---")
print(rollingTransId)
# --- main ---
# version_0(df) # original # doesn't work
version_1_a(df) # for-loop # OK
version_1_b(df) # for-loop # OK
version_2(df) # Series # OK
# version_3(df) # DataFrame # doesn't work
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。