我有一个pandas数据帧,其日期和字符串与此类似:
Start End Note Item
2016-10-22 2016-11-05 Z A
2017-02-11 2017-02-25 W B
Run Code Online (Sandbox Code Playgroud)
我需要将它扩展/转换为下面的内容,在Start和End列之间填写几周(W-SAT)并转发填写Note和Items中的数据:
Start Note Item
2016-10-22 Z A
2016-10-29 Z A
2016-11-05 Z A
2017-02-11 W B
2017-02-18 W B
2017-02-25 W B
Run Code Online (Sandbox Code Playgroud)
什么是与熊猫一起做到这一点的最好方法?某种多指数适用?
Ted*_*rou 15
您可以迭代每一行并创建一个新的数据帧,然后将它们连接在一起
pd.concat([pd.DataFrame({'Start': pd.date_range(row.Start, row.End, freq='W-SAT'),
'Note': row.Note,
'Item': row.Item}, columns=['Start', 'Note', 'Item'])
for i, row in df.iterrows()], ignore_index=True)
Start Note Item
0 2016-10-22 Z A
1 2016-10-29 Z A
2 2016-11-05 Z A
3 2017-02-11 W B
4 2017-02-18 W B
5 2017-02-25 W B
Run Code Online (Sandbox Code Playgroud)
小智 8
您根本不需要迭代。
df_start_end = df.melt(id_vars=['Note','Item'],value_name='date')
df = df_start_end.groupby('Note').apply(lambda x: x.set_index('date').resample('W').pad()).drop(columns=['Note','variable']).reset_index()
Run Code Online (Sandbox Code Playgroud)
小智 7
因此,我最近花了一些时间试图找出一种有效的pandas基于 - 的方法来解决这个问题(这对于data.tablein来说非常简单R),并想在这里分享我想到的方法:
df.set_index("Note").apply(
lambda row: pd.date_range(row["Start"], row["End"], freq="W-SAT").values, axis=1
).explode()
Run Code Online (Sandbox Code Playgroud)
注意:使用.values对性能有很大影响!
这里已经有很多解决方案,我想比较不同行数和周期的速度 - 请参阅下面的结果(以秒为单位):
pd.melt(),我将其df.set_index("date").groupby("Note").resample("W-SAT").ffill()标记为 Gen2,它的性能似乎稍好一些,并给出了相同的结果无论如何,当行数较多且句点较少时, jwdink 的解决方案看起来像是赢家,而我的解决方案在另一端似乎更好,尽管随着行数的减少,仅略微领先于其他解决方案:
| n_行 | n_周期 | 尤丁克 | 特德·佩特鲁 | 根 | 第二代 | 罗比 |
|---|---|---|---|---|---|---|
| 250 | 4000 | 6.63 | 0.33 | 0.64 | 0.45 | 0.28 |
| 500 | 2000年 | 3.21 | 0.65 | 1.18 | 0.81 | 0.34 |
| 1000 | 1000 | 1.57 | 1.28 | 2.30 | 1.60 | 0.48 |
| 2000年 | 500 | 0.83 | 2.57 | 4.68 | 3.24 | 0.71 |
| 5000 | 200 | 0.40 | 6.10 | 13.26 | 9.59 | 1.43 |
如果您想对此运行自己的测试,可以在我的GitHub 存储库中找到我的代码- 请注意,我创建了一个DateExpander类对象来包装所有函数,以便更轻松地扩展模拟。
另外,作为参考,我使用了 2 核 STANDARD_DS11_V2 Azure VM - 仅使用了大约 10 分钟,所以这实际上是我在这个问题上给出了 2 美分!
如果 的唯一值的数量df['End'] - df['Start']不太大,但数据集中的行数很大,那么以下函数将比循环数据集快得多:
def date_expander(dataframe: pd.DataFrame,
start_dt_colname: str,
end_dt_colname: str,
time_unit: str,
new_colname: str,
end_inclusive: bool) -> pd.DataFrame:
td = pd.Timedelta(1, time_unit)
# add a timediff column:
dataframe['_dt_diff'] = dataframe[end_dt_colname] - dataframe[start_dt_colname]
# get the maximum timediff:
max_diff = int((dataframe['_dt_diff'] / td).max())
# for each possible timediff, get the intermediate time-differences:
df_diffs = pd.concat([pd.DataFrame({'_to_add': np.arange(0, dt_diff + end_inclusive) * td}).assign(_dt_diff=dt_diff * td)
for dt_diff in range(max_diff + 1)])
# join to the original dataframe
data_expanded = dataframe.merge(df_diffs, on='_dt_diff')
# the new dt column is just start plus the intermediate diffs:
data_expanded[new_colname] = data_expanded[start_dt_colname] + data_expanded['_to_add']
# remove start-end cols, as well as temp cols used for calculations:
to_drop = [start_dt_colname, end_dt_colname, '_to_add', '_dt_diff']
if new_colname in to_drop:
to_drop.remove(new_colname)
data_expanded = data_expanded.drop(columns=to_drop)
# don't modify dataframe in place:
del dataframe['_dt_diff']
return data_expanded
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6140 次 |
| 最近记录: |