如何获得 pandas 数据框相同大小的常规条目列表?
How can I get a list of regular entries of the same size for pandas dataframe?
我有一个包含 [date, name, size]
列的数据框
我想要一个包含列的数据框
[name, type, size, dates]
为 amount
设置 tolerate-level
threshold = 0.02
date
上的一些 pre-processing 以提取有用的列
df['date'] = pd.to_datetime(df['date'])
df['amount'] = df['amount'].astype(float)
df['str_date'] = df['date'].dt.date.astype(str)
df['dayofmonth'] = df['date'].dt.day
df['month'] = df['date'].dt.year*12 + df['date'].dt.month
df['dayofweek'] = df['date'].dt.dayofweek
df['weeknum'] = (df['date'] - pd.to_datetime('2022-02-06')).dt.days // 7
定义辅助函数。
将 numpy 导入为 np
从 itertools 导入 groupby
def splitter(seq, threshold):
i, amount_base = 0, seq[0][1]
for j, r in enumerate(seq):
if abs(r[1]/amount_base-1) > threshold:
if j - i >= 3:
yield i
i, amount_base = j, r[1]
def cont_seqs(df, column, threshold):
'''column: 'weeknum' for weekly, 'month' for monthly'''
# weekly or hourly continuity
df['continuity'] = df[column].values - np.arange(len(df))
seqs = [
list(map(lambda r: (r.str_date, r.amount) , rs))
for _, rs in groupby(df.itertuples(), lambda r: r.continuity)
]
# amount threshold and minimum length
return [
dict(zip(('dates', 'amount'), list(zip(*segment))))
for seq in seqs
for segment in np.split(seq, list(splitter(seq, threshold)))
if len(segment) >= 3
]
首先,我们将发现所有 weekly
模式。
因为每周模式共享相同的 dayofweek
(例如,它们都是星期三),所以我们的想法是 groupby
具有 name
和 dayofweek
的数据框。
下一步是在一组中找出所有有效的日期序列为“weekly-continuous”。为此,我们使用 continuous_sequences(...)
。有效序列是具有 >=3 个日期的序列,并且所有金额值相对于第一个金额都在 tolerate-level 范围内。
由于一组可以包含多个有效序列,我们explode
这样一个序列占一行。但是,一组可能根本没有有效序列,因此我们将使用 dropna
删除它们。
方法的重置负责格式化。
weekly = df.groupby(['name', 'dayofweek'])\
.apply(lambda df: list(cont_seqs(df, 'weeknum', .02)))\
.explode()\
.dropna()\
.apply(pd.Series)\
.reset_index()\
.drop(columns='dayofweek')\
.assign(recurring_type='weekly')\
.reindex(['name', 'recurring_type', 'amount', 'dates'], axis=1)
然后,我们将发现所有的monthly
模式。
这一次,每月模式中的日期应该共享相同的 dayofmonth
,这就是我们将其放入 groupby
的原因。我们遵循与 weekly
模式类似的逻辑。
monthly = df.groupby(['name', 'dayofmonth'])\
.apply(lambda df: list(cont_seqs(df, 'month', .02)))\
.explode()\
.dropna()\
.apply(pd.Series)\
.reset_index()\
.drop(columns='dayofmonth')\
.assign(recurring_type='monthly')\
.reindex(['name', 'recurring_type', 'amount', 'dates'], axis=1)
最后,我们将两种模式组合成一个dataframe
pd.concat([weekly, monthly]).reset_index(drop=True)
结果
name recurring_type amount dates
0 John weekly (10.0, 10.0, 10.0) (2021-07-01, 2021-07-08, 2021-07-15)
1 John monthly (10.0, 10.04, 10.0) (2021-10-01, 2021-11-01, 2021-12-01)
我有一个包含 [date, name, size]
我想要一个包含列的数据框
[name, type, size, dates]
为 amount
threshold = 0.02
date
上的一些 pre-processing 以提取有用的列
df['date'] = pd.to_datetime(df['date'])
df['amount'] = df['amount'].astype(float)
df['str_date'] = df['date'].dt.date.astype(str)
df['dayofmonth'] = df['date'].dt.day
df['month'] = df['date'].dt.year*12 + df['date'].dt.month
df['dayofweek'] = df['date'].dt.dayofweek
df['weeknum'] = (df['date'] - pd.to_datetime('2022-02-06')).dt.days // 7
定义辅助函数。
将 numpy 导入为 np 从 itertools 导入 groupby
def splitter(seq, threshold):
i, amount_base = 0, seq[0][1]
for j, r in enumerate(seq):
if abs(r[1]/amount_base-1) > threshold:
if j - i >= 3:
yield i
i, amount_base = j, r[1]
def cont_seqs(df, column, threshold):
'''column: 'weeknum' for weekly, 'month' for monthly'''
# weekly or hourly continuity
df['continuity'] = df[column].values - np.arange(len(df))
seqs = [
list(map(lambda r: (r.str_date, r.amount) , rs))
for _, rs in groupby(df.itertuples(), lambda r: r.continuity)
]
# amount threshold and minimum length
return [
dict(zip(('dates', 'amount'), list(zip(*segment))))
for seq in seqs
for segment in np.split(seq, list(splitter(seq, threshold)))
if len(segment) >= 3
]
首先,我们将发现所有 weekly
模式。
因为每周模式共享相同的 dayofweek
(例如,它们都是星期三),所以我们的想法是 groupby
具有 name
和 dayofweek
的数据框。
下一步是在一组中找出所有有效的日期序列为“weekly-continuous”。为此,我们使用 continuous_sequences(...)
。有效序列是具有 >=3 个日期的序列,并且所有金额值相对于第一个金额都在 tolerate-level 范围内。
由于一组可以包含多个有效序列,我们explode
这样一个序列占一行。但是,一组可能根本没有有效序列,因此我们将使用 dropna
删除它们。
方法的重置负责格式化。
weekly = df.groupby(['name', 'dayofweek'])\
.apply(lambda df: list(cont_seqs(df, 'weeknum', .02)))\
.explode()\
.dropna()\
.apply(pd.Series)\
.reset_index()\
.drop(columns='dayofweek')\
.assign(recurring_type='weekly')\
.reindex(['name', 'recurring_type', 'amount', 'dates'], axis=1)
然后,我们将发现所有的monthly
模式。
这一次,每月模式中的日期应该共享相同的 dayofmonth
,这就是我们将其放入 groupby
的原因。我们遵循与 weekly
模式类似的逻辑。
monthly = df.groupby(['name', 'dayofmonth'])\
.apply(lambda df: list(cont_seqs(df, 'month', .02)))\
.explode()\
.dropna()\
.apply(pd.Series)\
.reset_index()\
.drop(columns='dayofmonth')\
.assign(recurring_type='monthly')\
.reindex(['name', 'recurring_type', 'amount', 'dates'], axis=1)
最后,我们将两种模式组合成一个dataframe
pd.concat([weekly, monthly]).reset_index(drop=True)
结果
name recurring_type amount dates
0 John weekly (10.0, 10.0, 10.0) (2021-07-01, 2021-07-08, 2021-07-15)
1 John monthly (10.0, 10.04, 10.0) (2021-10-01, 2021-11-01, 2021-12-01)