<p><strong>半矢量化方式</strong></p>
<p>正如我在下面所说的,我不认为有一种纯粹的矢量化方法可以将变量和一般的<code>DateOffset</code>添加到<code>Timestamp</code>的<code>Series</code>中@perl解决方案适用于<code>DateOffset</code>是1个月的精确倍数的情况</p>
<p>现在,添加单个常量<code>DateOffset</code><em>是</em>矢量化的,因此我们可以使用以下内容。它利用了一个事实,即日期偏移量有一组有限的不同值。它也相对较快,对于任何<code>DateOffset</code>和日期都是正确的:</p>
<pre class="lang-py prettyprint-override"><code>n = df['periods'].values
period_no = np.repeat(n - n.cumsum(), n) + np.arange(n.sum())
z = pd.DataFrame(
np.repeat(df.reset_index().values, repeats=n, axis=0),
columns=df.reset_index().columns,
).set_index('index')
z = z.assign(madd=period_no * z['interval'])
z['sdates'] = z['xdate']
for madd in set(z['madd'].unique()):
z.loc[z['madd'] == madd, 'sdates'] += pd.DateOffset(months=madd)
</code></pre>
<p>时间:</p>
<pre class="lang-py prettyprint-override"><code># modified large dummy data:
N = 170_000
todays_date = datetime.datetime.now().date()
xdates = pd.date_range(todays_date-datetime.timedelta(10), periods=N, freq='H')
categories = np.random.choice(list('ABCDE'), N)
d = {'xdate': xdates, 'periods': np.random.randint(1,10,N), 'interval': np.random.randint(1,12,N)}
df = pd.DataFrame(d,index=categories)
%%time (the above)
CPU times: user 3.49 s, sys: 13.5 ms, total: 3.51 s
Wall time: 3.51 s
</code></pre>
<p>(注意:对于使用上述生成的10K行,我看到的时间约为240ms,但这当然取决于数据中有多少不同的月份偏移)</p>
<p>示例结果(对于上述170K行的一次绘制):</p>
<pre class="lang-py prettyprint-override"><code>>>> z.tail()
xdate periods interval madd sdates
index
B 2040-08-25 06:00:00 8 8 48 2044-08-25 06:00:00
B 2040-08-25 06:00:00 8 8 56 2045-04-25 06:00:00
D 2040-08-25 07:00:00 3 2 0 2040-08-25 07:00:00
D 2040-08-25 07:00:00 3 2 2 2040-10-25 07:00:00
D 2040-08-25 07:00:00 3 2 4 2040-12-25 07:00:00
</code></pre>
<p><strong>对初始答案的更正</strong></p>
<p>我的观点是正确的:我的原始答案也没有矢量化。第一部分,分解数据帧并构建要添加的月数,是矢量化的,速度非常快。但第二部分,增加了一个月数可变的<code>DateOffset</code>,则不是</p>
<p>我希望我错了,但我不认为目前有一种方法可以以矢量化的方式完成第二部分</p>
<p>直接日期部分操作(例如<code>month = (month - 1 + n_months) % 12 + 1</code>等)对于转角情况(例如<code>'2021-02-31'</code>)注定会失败。除了复制<code>DateOffset</code>中使用的逻辑之外,这在某些情况下是行不通的</p>
<p><strong>初始答案</strong></p>
<p>以下是一种矢量化方式:</p>
<pre class="lang-py prettyprint-override"><code>n = df.periods.values
period_no = np.repeat(n - n.cumsum(), n) + np.arange(n.sum())
z = pd.DataFrame(
np.repeat(df.reset_index().values, repeats=n, axis=0),
columns=df.reset_index().columns,
).set_index('index').assign(period_no=period_no)
z['sdates'] = z['period_no'] * z['interval'] * pd.DateOffset(months=1) + z['xdate']
</code></pre>