结合两个不同数据帧的矢量化 for 循环
Vectorize for-loop which combines two different dataframes
我有一个包含输入数据的两列数据框。
第一列是开始日期,第二列称为持续时间(分钟)。你可以想象一台机器从开始运行到开始+持续时间。
我想使用此信息构建一个长度为 8760*60 的一维数组,其中包含一年中的所有分钟,机器运行的地方应该有一个 1,否则应该有一个零。
下面的 MWE 完成了任务,但由于 for 循环而速度很慢,我不知道如何对其进行矢量化。
import pandas as pd
import numpy as np
# Start and end of time horizon
start = pd.Timestamp(year=2019, month=1, day=1, hour=0, tz='UTC')
end = pd.Timestamp(year=2019, month=12, day=31, hour=23, minute=59, tz='UTC')
# DataFrame of time horizon
dates = pd.DataFrame(pd.date_range(start, end, freq='min'))
# Starting points
t1 = pd.Timestamp(year=2019, month=1, day=2, hour=0, tz='UTC')
t2 = pd.Timestamp(year=2019, month=1, day=1, hour=0, minute=3, tz='UTC')
# Durations
d1 = 5
d2 = 30
# DataFrame from input data
data = pd.DataFrame(
data=[
[t1, d1],
[t2, d2],
],
columns=[
'start',
'duration',
]
)
# Array to be filled
on = np.zeros(8760*60)
# loop over data rows
for idx in data.index:
# Start for on array from dates
start = dates[dates[0] == data.loc[idx, 'start']].index[0]
# Duration from data
duration = data.loc[idx, 'duration']
# Put 1s in the on array from start to start+duration
on[start: start+duration] = 1
这对你有用吗:
idx = pd.date_range(pd.Timestamp('2019-01-01', tz='UTC'),
pd.Timestamp('2019-12-31', tz='UTC'),
freq='1min')
df = pd.DataFrame({'on': 0}, index=idx)
def to_mins(row):
return set(pd.date_range(row['start'], periods=row['duration'], freq='1min'))
idx_on = set().union(*data[['start', 'duration']].apply(to_mins, axis='columns'))
df.loc[idx_on] = 1
on = df.on.values
如果持续时间可能导致 2019 年以外的时间戳,您可以使用:
def to_min_range(row):
return set(
m
for m in pd.date_range(row['start'], periods=row['duration'], freq='1min')
if m.year == 2019
)
idx = pd.date_range(start, end, freq="min").to_series()
data["duration"] = pd.to_timedelta(data.duration, unit="min")
res = pd.concat(
[
i.to_series()
for i in data.apply(
lambda row: pd.date_range(row[0], row.sum(), freq="min", closed="right"),
axis=1,
)
]
)
_, res = idx.align(res)
on = (~res.isna()).astype("int").values
这也使用 apply
len(data)
次,希望这个数字比一年的分钟数少得多。
编辑:
两端的时间间隔都是封闭的。在 OP 提到的评论中,他们希望总和与设备开启的总持续时间相匹配,这可以通过打开间隔的一端来实现。由于总和预计是持续时间,我选择了 right
.
我有一个包含输入数据的两列数据框。 第一列是开始日期,第二列称为持续时间(分钟)。你可以想象一台机器从开始运行到开始+持续时间。 我想使用此信息构建一个长度为 8760*60 的一维数组,其中包含一年中的所有分钟,机器运行的地方应该有一个 1,否则应该有一个零。 下面的 MWE 完成了任务,但由于 for 循环而速度很慢,我不知道如何对其进行矢量化。
import pandas as pd
import numpy as np
# Start and end of time horizon
start = pd.Timestamp(year=2019, month=1, day=1, hour=0, tz='UTC')
end = pd.Timestamp(year=2019, month=12, day=31, hour=23, minute=59, tz='UTC')
# DataFrame of time horizon
dates = pd.DataFrame(pd.date_range(start, end, freq='min'))
# Starting points
t1 = pd.Timestamp(year=2019, month=1, day=2, hour=0, tz='UTC')
t2 = pd.Timestamp(year=2019, month=1, day=1, hour=0, minute=3, tz='UTC')
# Durations
d1 = 5
d2 = 30
# DataFrame from input data
data = pd.DataFrame(
data=[
[t1, d1],
[t2, d2],
],
columns=[
'start',
'duration',
]
)
# Array to be filled
on = np.zeros(8760*60)
# loop over data rows
for idx in data.index:
# Start for on array from dates
start = dates[dates[0] == data.loc[idx, 'start']].index[0]
# Duration from data
duration = data.loc[idx, 'duration']
# Put 1s in the on array from start to start+duration
on[start: start+duration] = 1
这对你有用吗:
idx = pd.date_range(pd.Timestamp('2019-01-01', tz='UTC'),
pd.Timestamp('2019-12-31', tz='UTC'),
freq='1min')
df = pd.DataFrame({'on': 0}, index=idx)
def to_mins(row):
return set(pd.date_range(row['start'], periods=row['duration'], freq='1min'))
idx_on = set().union(*data[['start', 'duration']].apply(to_mins, axis='columns'))
df.loc[idx_on] = 1
on = df.on.values
如果持续时间可能导致 2019 年以外的时间戳,您可以使用:
def to_min_range(row):
return set(
m
for m in pd.date_range(row['start'], periods=row['duration'], freq='1min')
if m.year == 2019
)
idx = pd.date_range(start, end, freq="min").to_series()
data["duration"] = pd.to_timedelta(data.duration, unit="min")
res = pd.concat(
[
i.to_series()
for i in data.apply(
lambda row: pd.date_range(row[0], row.sum(), freq="min", closed="right"),
axis=1,
)
]
)
_, res = idx.align(res)
on = (~res.isna()).astype("int").values
这也使用 apply
len(data)
次,希望这个数字比一年的分钟数少得多。
编辑:
两端的时间间隔都是封闭的。在 OP 提到的评论中,他们希望总和与设备开启的总持续时间相匹配,这可以通过打开间隔的一端来实现。由于总和预计是持续时间,我选择了 right
.