Ceci est un message auto-répondu. Un problème courant consiste à générer de manière aléatoire des dates entre une date de début et une date de fin données.
Il y a deux cas à considérer:
Par exemple, étant donné une date de début 2015-01-01
et une date de fin 2018-01-01
, comment puis-je échantillonner N dates aléatoires entre cette plage en utilisant des pandas?
Nous pouvons accélérer l'approche de @ akilat90 en deux étapes (dans le test de référence de @ coldspeed) en utilisant le fait que datetime64
est simplement un int64
renommé, d'où nous pouvons visionner le casting:
def pp(start, end, n):
start_u = start.value//10**9
end_u = end.value//10**9
return pd.DatetimeIndex((10**9*np.random.randint(start_u, end_u, n)).view('M8[ns]'))
La conversion vers l'horodatage Unix est-elle acceptable?
def random_dates(start, end, n=10):
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')
Échantillon échantillon:
start = pd.to_datetime('2015-01-01')
end = pd.to_datetime('2018-01-01')
random_dates(start, end)
DatetimeIndex(['2016-10-08 07:34:13', '2015-11-15 06:12:48',
'2015-01-24 10:11:04', '2015-03-26 16:23:53',
'2017-04-01 00:38:21', '2015-05-15 03:47:54',
'2015-06-24 07:32:32', '2015-11-10 20:39:36',
'2016-07-25 05:48:09', '2015-03-19 16:05:19'],
dtype='datetime64[ns]', freq=None)
MODIFIER:
Selon le commentaire de @smci, j’ai écrit une fonction permettant d’adapter à la fois les 1 et les 2 avec une petite explication dans la fonction elle-même.
def random_datetimes_or_dates(start, end, out_format='datetime', n=10):
'''
unix timestamp is in ns by default.
I divide the unix time value by 10**9 to make it seconds (or 24*60*60*10**9 to make it days).
The corresponding unit variable is passed to the pd.to_datetime function.
Values for the (divide_by, unit) pair to select is defined by the out_format parameter.
for 1 -> out_format='datetime'
for 2 -> out_format=anything else
'''
(divide_by, unit) = (10**9, 's') if out_format=='datetime' else (24*60*60*10**9, 'D')
start_u = start.value//divide_by
end_u = end.value//divide_by
return pd.to_datetime(np.random.randint(start_u, end_u, n), unit=unit)
Échantillon échantillon:
random_datetimes_or_dates(start, end, out_format='datetime')
DatetimeIndex(['2017-01-30 05:14:27', '2016-10-18 21:17:16',
'2016-10-20 08:38:02', '2015-09-02 00:03:08',
'2015-06-04 02:38:12', '2016-02-19 05:22:01',
'2015-11-06 10:37:10', '2017-12-17 03:26:02',
'2017-11-20 06:51:32', '2016-01-02 02:48:03'],
dtype='datetime64[ns]', freq=None)
random_datetimes_or_dates(start, end, out_format='not datetime')
DatetimeIndex(['2017-05-10', '2017-12-31', '2017-11-10', '2015-05-02',
'2016-04-11', '2015-11-27', '2015-03-29', '2017-05-21',
'2015-05-11', '2017-02-08'],
dtype='datetime64[ns]', freq=None)
np.random.randn
+ to_timedelta
Ceci concerne le cas (1). Vous pouvez le faire en générant un tableau aléatoire d'objets timedelta
et en les ajoutant à votre date 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)
Cela générera des dates avec une composante heure.
Malheureusement, Rand
ne prend pas en charge un replace=False
. Par conséquent, si vous souhaitez des dates uniques, vous aurez besoin d’un processus en deux étapes.
Et additionnez les deux.
np.random.randint
+ to_timedelta
Ceci concerne le cas (2). Vous pouvez modifier random_dates
ci-dessus pour générer des entiers aléatoires à la place des nombres flottants aléatoires:
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)
Pour générer des dates avec d'autres fréquences, les fonctions ci-dessus peuvent être appelées avec une valeur différente pour unit
. De plus, vous pouvez ajouter un paramètre freq
et ajuster votre appel de fonction si nécessaire.
Si vous voulez unique} _ dates aléatoires, vous pouvez utiliser np.random.choice
avec 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
)
Performance
Nous allons juste comparer les méthodes qui traitent Case (1), car Case (2) est vraiment un cas particulier que toute méthode peut utiliser pour utiliser 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]
Code d'analyse comparative des performances
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()
numpy.random.choice
Vous pouvez tirer parti du choix aléatoire de Numpy. choice
peut être problématique par rapport au data_ranges
grand. Par exemple, trop grand entraînera une MemoryError. Cela nécessite de stocker le tout afin de sélectionner des bits aléatoires.
random_dates('2015-01-01', '2018-01-01', 10, 'ns', seed=[3, 1415])
MemoryError
En outre, cela nécessite une sorte.
def random_dates(start, end, n, freq, seed=None):
if seed is not None:
np.random.seed(seed)
dr = pd.date_range(start, end, freq=freq)
return pd.to_datetime(np.sort(np.random.choice(dr, n, replace=False)))
random_dates('2015-01-01', '2018-01-01', 10, 'H', seed=[3, 1415])
DatetimeIndex(['2015-04-24 02:00:00', '2015-11-26 23:00:00',
'2016-01-18 00:00:00', '2016-06-27 22:00:00',
'2016-08-12 17:00:00', '2016-10-21 11:00:00',
'2016-11-07 11:00:00', '2016-12-09 23:00:00',
'2017-02-20 01:00:00', '2017-06-17 18:00:00'],
dtype='datetime64[ns]', freq=None)
numpy.random.permutation
Semblable à une autre réponse. Cependant, j'aime cette réponse car elle découpe la datetimeindex
produite par date_range
et renvoie automatiquement une autre datetimeindex
.
def random_dates_2(start, end, n, freq, seed=None):
if seed is not None:
np.random.seed(seed)
dr = pd.date_range(start, end, freq=freq)
a = np.arange(len(dr))
b = np.sort(np.random.permutation(a)[:n])
return dr[b]
J'ai trouvé Une nouvelle bibliothèque de base générant la plage de la date, semble de mon côté un peu plus rapide que pandas.data_range
, crédit de cette answer
from dateutil.rrule import rrule, DAILY
import datetime, random
def pick(start,end,n):
return (random.sample(list(rrule(DAILY, dtstart=start,until=end)),n))
pick(datetime.datetime(2010, 2, 1, 0, 0),datetime.datetime(2010, 2, 5, 0, 0),2)
[datetime.datetime(2010, 2, 3, 0, 0), datetime.datetime(2010, 2, 2, 0, 0)]
Juste mes deux cents, en utilisant date_range et sample:
def random_dates(start, end, n, seed=1, replace=False):
dates = pd.date_range(start, end).to_series()
return dates.sample(n, replace=replace, random_state=seed)
random_dates("20170101","20171223", 10, seed=1)
Out[29]:
2017-10-01 2017-10-01
2017-08-23 2017-08-23
2017-11-30 2017-11-30
2017-06-15 2017-06-15
2017-11-18 2017-11-18
2017-10-31 2017-10-31
2017-07-31 2017-07-31
2017-03-07 2017-03-07
2017-09-09 2017-09-09
2017-10-15 2017-10-15
dtype: datetime64[ns]
Je pense que c'est une solution plus facile pour créer un champ de date dans un pandas. DateFrame
list1 = []
for x in range(0,365):
list1.append(x)
date = pd.DataFrame(pd.to_datetime(list1, unit='D',Origin=pd.Timestamp('2018-01-01')))
C’est une alternative: D Peut-être que quelqu'un en aura besoin.
from datetime import datetime
import random
import numpy as np
import pandas as pd
N = 10 #N-samples
dates = np.zeros([N,3])
for i in range(0,N):
year = random.randint(1970, 2010)
month = random.randint(1, 12)
day = random.randint(1, 28)
#if you need to change it use variables :3
birth_date = datetime(year, month, day)
dates[i] = [year,month,day]
df = pd.DataFrame(dates.astype(int))
df.columns = ['year', 'month', 'day']
pd.to_datetime(df)
Résultat:
0 1999-08-22
1 1989-04-27
2 1978-10-01
3 1998-12-09
4 1979-04-19
5 1988-03-22
6 1992-03-02
7 1993-04-28
8 1978-10-04
9 1972-01-13
dtype: datetime64[ns]