np.random.randn
+ to_timedelta
Это адрес Case (1).Вы можете сделать это, сгенерировав случайный массив из timedelta
объектов и добавив их к вашей start
дате.
def random_dates(start, end, n, unit='D', seed=None):
if not seed: # from piR's answer
np.random.seed(0)
ndays = (end - start).days + 1
return pd.to_timedelta(np.random.rand(n) * ndays, unit=unit) + start
>>> np.random.seed(0)
>>> start = pd.to_datetime('2015-01-01')
>>> end = pd.to_datetime('2018-01-01')
>>> random_dates(start, end, 10)
DatetimeIndex([ '2016-08-25 01:09:42.969600',
'2017-02-23 13:30:20.304000',
'2016-10-23 05:33:15.033600',
'2016-08-20 17:41:04.012799999',
'2016-04-09 17:59:00.815999999',
'2016-12-09 13:06:00.748800',
'2016-04-25 00:47:45.974400',
'2017-09-05 06:35:58.444800',
'2017-11-23 03:18:47.347200',
'2016-02-25 15:14:53.894400'],
dtype='datetime64[ns]', freq=None)
Это создаст даты с компонентом временитакже.
К сожалению, rand
не поддерживает replace=False
, поэтому, если вам нужны уникальные даты, вам потребуется двухэтапный процесс: 1) создать компонент неуникальных дней и 2) создатьуникальный компонент секунд / миллисекунд, затем сложите их вместе.
np.random.randint
+ to_timedelta
Это относится к случаю (2).Вы можете изменить random_dates
выше, чтобы генерировать случайные целые числа вместо случайных чисел:
def random_dates2(start, end, n, unit='D', seed=None):
if not seed: # from piR's answer
np.random.seed(0)
ndays = (end - start).days + 1
return start + pd.to_timedelta(
np.random.randint(0, ndays, n), unit=unit
)
>>> random_dates2(start, end, 10)
DatetimeIndex(['2016-11-15', '2016-07-13', '2017-04-15', '2017-02-02',
'2017-10-30', '2015-10-05', '2016-08-22', '2017-12-30',
'2016-08-23', '2015-11-11'],
dtype='datetime64[ns]', freq=None)
Чтобы генерировать даты с другими частотами, указанные выше функции можно вызывать с другойзначение для unit
.Кроме того, вы можете добавить параметр freq
и настроить вызов функции по необходимости.
Если вы хотите уникальные случайные даты, вы можете использовать np.random.choice
с replace=False
:
def random_dates2_unique(start, end, n, unit='D', seed=None):
if not seed: # from piR's answer
np.random.seed(0)
ndays = (end - start).days + 1
return start + pd.to_timedelta(
np.random.choice(ndays, n, replace=False), unit=unit
)
Производительность
Идем для сравнения только методов, которые обращаются к Case (1), поскольку Case (2) действительно особый случай, который любой метод можетприступить к использованию dt.floor
.
Функции
def cs(start, end, n):
ndays = (end - start).days + 1
return pd.to_timedelta(np.random.rand(n) * ndays, unit='D') + start
def akilat90(start, end, n):
start_u = start.value//10**9
end_u = end.value//10**9
return pd.to_datetime(np.random.randint(start_u, end_u, n), unit='s')
def piR(start, end, n):
dr = pd.date_range(start, end, freq='H') # can't get better than this :-(
return pd.to_datetime(np.sort(np.random.choice(dr, n, replace=False)))
def piR2(start, end, n):
dr = pd.date_range(start, end, freq='H')
a = np.arange(len(dr))
b = np.sort(np.random.permutation(a)[:n])
return dr[b]
Код бенчмаркинга
from timeit import timeit
import pandas as pd
import matplotlib.pyplot as plt
res = pd.DataFrame(
index=['cs', 'akilat90', 'piR', 'piR2'],
columns=[10, 20, 50, 100, 200, 500, 1000, 2000, 5000],
dtype=float
)
for f in res.index:
for c in res.columns:
np.random.seed(0)
start = pd.to_datetime('2015-01-01')
end = pd.to_datetime('2018-01-01')
stmt = '{}(start, end, c)'.format(f)
setp = 'from __main__ import start, end, c, {}'.format(f)
res.at[f, c] = timeit(stmt, setp, number=30)
ax = res.div(res.min()).T.plot(loglog=True)
ax.set_xlabel("N");
ax.set_ylabel("time (relative)");
plt.show()