Skip to content

Commit 621a6b5

Browse files
committed
perf(core): vectorize the date conversion in basemodel_to_df
basemodel_to_df ran three per-element Python passes over the date column of every payload it converted. Series.apply(to_datetime) calls pandas.to_datetime once per row, and because each call receives a lone scalar with no format hint, pandas also re-runs its datetime format guesser on every row. Parse the column in one call instead, and use vectorized comparisons for the all-midnight test and the reduction to datetime.date. The element-wise parse is kept as a fallback for one case that the vectorized path cannot represent: a column mixing UTC offsets, such as a series crossing a daylight-savings boundary. Parsing per element lets each value keep its own offset and yields an object column of tz-aware Timestamps, whereas a single to_datetime call over that column raises "Mixed timezones detected". That behaviour is covered by test_to_df_daylight_savings and is preserved exactly. Measured on a payload built through EquityHistoricalData, median of 9 under an exclusive CPU lock: 250 daily bars 8.92 ms -> 1.46 ms 1,000 daily bars 32.93 ms -> 3.13 ms 5,000 daily bars 162.25 ms -> 12.23 ms
1 parent 3e071fc commit 621a6b5

1 file changed

Lines changed: 16 additions & 3 deletions

File tree

  • openbb_platform/core/openbb_core/app

openbb_platform/core/openbb_core/app/utils.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,22 @@ def basemodel_to_df(
4444

4545
# If the date column contains dates only, convert them to a date to avoid encoding time data.
4646
if "date" in df.columns:
47-
df["date"] = df["date"].apply(to_datetime)
48-
if all(t.time() == time(0, 0) for t in df["date"]):
49-
df["date"] = df["date"].apply(lambda x: x.date())
47+
try:
48+
# Parse the column in a single call instead of once per row.
49+
parsed = to_datetime(df["date"])
50+
except ValueError:
51+
# A column that mixes UTC offsets - a series crossing a daylight
52+
# savings boundary, for instance - has no single datetime64
53+
# representation, so fall back to parsing each value on its own.
54+
parsed = df["date"].apply(to_datetime)
55+
56+
df["date"] = parsed
57+
58+
if parsed.dtype == object:
59+
if all(t.time() == time(0, 0) for t in parsed):
60+
df["date"] = parsed.apply(lambda x: x.date())
61+
elif (parsed == parsed.dt.normalize()).all():
62+
df["date"] = parsed.dt.date
5063

5164
if index and index in df.columns:
5265
if index == "date":

0 commit comments

Comments
 (0)