Как генерировать синтетические данные со случайными значениями в кадре данных pandas?

У меня есть фрейм данных с 50 тыс. строк. Я хотел бы заменить 20% данных случайными значениями (с указанием интервала случайных чисел). Цель состоит в том, чтобы генерировать синтетические выбросы для тестирования алгоритмов. Следующий кадр данных - это небольшая часть df, которая у меня есть. Значения, которые следует заменить случайными выбросами, - это столбец «значение».

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

Например, я хочу указать интервал случайных значений от 1 до 50, и желаемый 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     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

Буду признателен за любые идеи. Спасибо!


person Sascha    schedule 29.12.2019    source источник
comment
Если я вас правильно понял, вы хотите заменить 20% значений в столбце 'value' случайными значениями от 1 до 50?   -  person Andrej Kesely    schedule 29.12.2019
comment
да. или с несколькими интервалами если можно например от 1 до 50 и от 200-300. Можно ли сделать это автоматически?   -  person Sascha    schedule 29.12.2019
comment
Использование dict в качестве имени переменной не рекомендуется, так как это встроенное имя, представляющее словарь.   -  person Marios Keri    schedule 29.12.2019


Ответы (5)


Вот пример numpy, который должен быть быстрым. В примере, который включает как более высокую, так и более низкую замену, предполагается, что вы хотите равномерно заменить высокие и низкие значения (50-50), если это не так, вы можете изменить p в mask_high = np.random.choice([0,1], p=[.5, .5], size=rand.shape).astype(np.bool) на все, что хотите.

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) 

# create a function
def myFunc(df, replace_pct, start_range, stop_range, replace_col):
    # create an array of the col you want to replace
    val = df[replace_col].values 
    # create a boolean mask for the percent you want to replace
    mask = np.random.choice([0,1], p=[1-replace_pct, replace_pct], size=val.shape).astype(np.bool)
    # create a random ints between the range
    rand = np.random.randint(start_range, stop_range, size=len(mask[mask == True]))
    # replace values in the original array
    val[mask] = rand
    # update column
    df[replace_col] = val
    return df

myFunc(df, .2, 1, 50, 'value')

         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     46
5  2016-11-11  03:00:00     94
6  2016-11-11  04:00:00     91
7  2016-11-11  04:00:00     94

время

%%timeit
myFunc(df, .2, 1, 50, 'value')

397 µs ± 27.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Пример как высокой, так и низкой замены

# create a function
def myFunc2(df, replace_pct, start_range_low, stop_range_low,
            start_range_high, stop_range_high, replace_col):
    # create array of col you want to replace
    val = df[replace_col].values 
    # create a boolean mask for the percent you want to replace
    mask = np.random.choice([0,1], p=[1-replace_pct, replace_pct], size=val.shape).astype(np.bool)
    # create a random int between ranges
    rand = np.random.randint(start_range_low, stop_range_low, size=len(mask[mask == True]))
    # create a mask for the higher range
    mask_high = np.random.choice([0,1], p=[.5, .5], size=rand.shape).astype(np.bool)
    # create random ints between high ranges
    rand_high = np.random.randint(start_range_high, stop_range_high, size=len(mask_high[mask_high == True]))
    # replace values in the rand array
    rand[mask_high] = rand_high
    # replace values in the original array
    val[mask] = rand
    # update column
    df[replace_col] = val
    return df

myFunc2(df, .2, 1, 50, 200, 300, 'value')


         date      time  value
0  2016-11-10  22:00:00     90
1  2016-11-10  23:00:00    216
2  2016-11-11  00:00:00     80
3  2016-11-11  01:00:00     49
4  2016-11-11  02:00:00     84
5  2016-11-11  03:00:00     94
6  2016-11-11  04:00:00    270
7  2016-11-11  04:00:00     94

время

%%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)
person It_is_Chris    schedule 29.12.2019
comment
Для тех, кто хочет сгенерировать кадр данных pandas с синтетическими данными контролируемым образом, взгляните на эту статью: towardsdatascience.com/ - person Juan Luis Ruiz-tagle; 31.07.2021

Еще одна попытка с использованием DataFrame.sample().

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)

Это печатает (например):

         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
person Andrej Kesely    schedule 29.12.2019
comment
Спасибо за подробный ответ. у меня вопрос по size=len(random_rows) // 2 + 1 части. если вы укажете количество случайных значений с помощью size=len(random_rows), то что делает // 2+1? - person Sascha; 30.12.2019
comment
@Саша len(random_rows) // 2 + 1 означает (len(random_rows) // 2) + 1. // - это этажное (или целочисленное) деление поэтому количество выбранных строк div 2 плюс 1. - person Andrej Kesely; 30.12.2019

Это может сработать.

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)
person Tunahan A.    schedule 29.12.2019

Аналогичный ответ с использованием sample :

Пример 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])
person Nathan Furnal    schedule 29.12.2019

Вот несколько шагов, которые вы можете использовать. Как отмечалось выше, вы НЕ должны использовать dict в качестве имени переменной. Я сделал ниже, потому что я просто скопировал ваш ввод кода.

Этот код генерирует список индексов на основе коэффициента замены и длины кадра данных, а затем заменяет значения в этих местах однородными случайными целыми числами от 0 до 20 включительно:

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]: 
person AirSquid    schedule 29.12.2019