J'ai un dataframe avec 50K lignes. Je voudrais remplacer 20% des données par des valeurs aléatoires (donnant un intervalle de nombres aléatoires). Le but est de générer des valeurs aberrantes synthétiques pour tester les algorithmes. Le dataframe suivant est une petite partie de df que j'ai. Les valeurs qui doivent être remplacées par des valeurs aberrantes aléatoires sont la colonne «valeur».
date time value 0 2016-11-10 22:00:00 90 1 2016-11-10 23:00:00 91 2 2016-11-11 00:00:00 80 3 2016-11-11 01:00:00 4 4 2016-11-11 02:00:00 84 5 2016-11-11 03:00:00 94 6 2016-11-11 04:00:00 32 7 2016-11-11 05:00:00 94
Par exemple, je veux donner un intervalle de valeurs aléatoires de 1 à 50, et le df souhaité ressemblerait à comme suit:
import pandas as pd dict = {'date':["2016-11-10", "2016-11-10", "2016-11-11", "2016-11-11","2016-11-11","2016-11-11","2016-11-11", "2016-11-11" ], 'time': ["22:00:00", "23:00:00", "00:00:00", "01:00:00", "02:00:00", "03:00:00", "04:00:00", "04:00:00"], 'value':[90, 91, 80, 87, 84,94, 91, 94]} df = pd.DataFrame(dict) print(df) date time value 0 2016-11-10 22:00:00 90 1 2016-11-10 23:00:00 91 2 2016-11-11 00:00:00 80 3 2016-11-11 01:00:00 87 4 2016-11-11 02:00:00 84 5 2016-11-11 03:00:00 94 6 2016-11-11 04:00:00 91 7 2016-11-11 05:00:00 94
J'apprécierais toutes les idées. Merci!
5 Réponses :
Voici quelques étapes que vous pouvez utiliser. Comme indiqué ci-dessus, vous ne devez PAS utiliser dict
comme nom de variable. Je l'ai fait ci-dessous, car je viens de copier vos entrées de code.
Ce code génère une liste d'indices en fonction du taux de remplacement et de la longueur de la trame de données, puis remplace les valeurs à ces emplacements par des entiers aléatoires uniformes de 0 à 20, inclusivement:
In [49]: # %load 32-36 ...: df=pd.DataFrame(dict) ...: import random ...: replacement_ratio = 0.50 ...: replacement_count = int(replacement_ratio * len(df)) ...: replacement_idx = random.sample(range(len(df)), replacement_count) In [50]: replacement_idx Out[50]: [5, 2, 7, 6] In [51]: for idx in replacement_idx: ...: df.loc[idx, 'value'] = random.randint(0,20) ...: In [52]: df Out[52]: date time value 0 2016-11-10 22:00:00 90 1 2016-11-10 23:00:00 91 2 2016-11-11 00:00:00 4 3 2016-11-11 01:00:00 87 4 2016-11-11 02:00:00 84 5 2016-11-11 03:00:00 4 6 2016-11-11 04:00:00 17 7 2016-11-11 04:00:00 8 In [53]:
Cela pourrait fonctionner.
outliers = [] def get_outlier(x): num = 3 mean_ = np.mean(x) std_ = np.std(x) for y in x: z_score = (y - mean_) / std_ if np.abs(z_score) > num: outliers.append(y) return get_outlier detect_outliers = get_outlier(df['value']) sorted(df['value']) q1, q3 = np.percentile(df['value'], [25, 75]) iqr = q3 - q1 lb = q1 - (1.5 * iqr) ub = q3 - (1.5 * iqr) for i in range(len(df)): if ((df['value'][i] < lb) | (df['value'][i] > ub)): df['value'][i] = np.random.randint(1, 50)
Voici un exemple de numpy
qui devrait être rapide. L'exemple qui inclut à la fois le remplacement supérieur et inférieur suppose que vous souhaitez remplacer les valeurs haute et basse de manière égale (50-50) si ce n'est pas le cas, vous pouvez modifier le p
dans mask_high = np.random.choice ([0,1], p = [. 5, .5], size = rand.shape) .astype (np.bool)
à ce que vous voulez.
%%timeit myFunc2(df, .2, 1, 50, 200, 300, 'value') 493 µs ± 41.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Une autre tentative, en utilisant DataFrame.sample ()
.
date time value 0 2016-11-10 22:00:00 31 <-- 31 1 2016-11-10 23:00:00 91 2 2016-11-11 00:00:00 80 3 2016-11-11 01:00:00 87 4 2016-11-11 02:00:00 84 5 2016-11-11 03:00:00 236 <-- 236 6 2016-11-11 04:00:00 91 7 2016-11-11 04:00:00 94
Ceci imprime (par exemple):
import numpy as np import pandas as pd d = {'date':["2016-11-10", "2016-11-10", "2016-11-11", "2016-11-11","2016-11-11","2016-11-11","2016-11-11", "2016-11-11" ], 'time': ["22:00:00", "23:00:00", "00:00:00", "01:00:00", "02:00:00", "03:00:00", "04:00:00", "04:00:00"], 'value':[90, 91, 80, 87, 84,94, 91, 94]} df = pd.DataFrame(d) random_rows = df.sample(frac=.2) # 20% random rows from `df` # we are replacing these 20% random rows with values from 1..50 and 200..300 (in ~1:1 ratio) random_values = np.random.choice( np.concatenate( [np.random.randint(1, 50, size=len(random_rows) // 2 + 1), np.random.randint(200, 300, size=len(random_rows) // 2 + 1)] ), size=len(random_rows) ) df.loc[random_rows.index, 'value'] = random_values print(df)
Merci pour une réponse détaillée. J'ai une question concernant la partie size = len (random_rows) // 2 + 1
. si vous spécifiez le nombre de valeurs aléatoires avec size = len (random_rows)
alors que fait // 2 + 1
?
@Sascha len (random_rows) // 2 + 1
signifie (len (random_rows) // 2) + 1
. //
correspond au étage ( ou entier) division donc nombre de lignes sélectionnées div 2 plus 1.
Réponse similaire en utilisant un exemple
:
Exemple df
:
import pandas as pd df = pd.DataFrame({"time_col" : pd.date_range("2018-01-01", "2019-01-01", freq = "H")}) df["date"], df["time"] = df["time_col"].dt.date, df["time_col"].dt.hour df["value"] = pd.np.random.randint(100, 150, df.shape[0]) seed = 11 # deterministic behavior, that's what heroes do rnd_rows_idx = df.sample(frac = 0.2, random_state=seed).index # grabbing indexes original_rows = df.loc[rnd_rows_idx, "value"] # keeping a trace of original values ### Replacing the values selected at random ### df.loc[rnd_rows_idx, "value"] = pd.np.random.randint(1, 50, rnd_rows_idx.shape[0])
Si je vous comprends bien, voulez-vous remplacer 20% des valeurs de la colonne
"valeur"
par des valeurs aléatoires comprises entre 1 et 50?Oui. ou à plusieurs intervalles si c'est possible par exemple de 1 à 50 et de 200 à 300. Est-il possible de le faire automatiquement?
L'utilisation de "dict" comme nom de variable n'est pas suggérée car il s'agit d'un nom intégré représentant un dictionnaire