Как генерировать случайные категориальные данные из существующих, чтобы заполнить пропущенные значения — Python

#python #pandas #data-cleaning #data-presentation

#python #pandas #очистка данных #данные-презентация

Вопрос:

У меня есть столбец с отсутствующими категориальными данными, и я пытаюсь заменить их существующими категориальными переменными из того же столбца.

Я не хочу использовать этот режим, потому что у меня слишком много недостающих данных, это приведет к искажению данных, и я бы предпочел не удалять строки с недостающими данными.

Я думаю, что идеальным способом было бы получить пропорцию каждой переменной для моего столбца, а затем пропорционально заменить недостающие данные существующими категориальными переменными.

Пример фрейма данных:

    ClientId    Apple_cat    Region    Price
0  21          cat_1        Reg_A     5
1  15          cat_2        Nan       6
2  6           Nan          Reg_B     7
3  91          cat_3        Reg_A     3
4  45          Nan          Reg_C     7
5  89          cat_2        Nan       6
  

Примечание: В идеале я бы хотел избежать жесткого кодирования каждой категории и названия региона.

Комментарии:

1. Не могли бы вы предоставить дополнительную информацию? Каков ваш ожидаемый результат? Что вы пробовали до сих пор?

2. Ожидаемый результат: заполнить недостающие данные пропорционально существующими данными.

Ответ №1:

Вы можете использовать свою собственную функцию для аккуратного и векторизованного метода решения этой проблемы:

 def na_randomfill(series):
    na_mask = pd.isnull(series)   # boolean mask for null values
    n_null = na_mask.sum()        # number of nulls in the Series
    
    if n_null == 0:
        return series             # if there are no nulls, no need to resample
    
    # Randomly sample the non-null values from our series
    #  only sample this Series as many times as we have nulls 
    fill_values = series[~na_mask].sample(n=n_null, replace=True, random_state=0)

    # This ensures our new values will replace NaNs in the correct locations
    fill_values.index = series.index[na_mask]
    
    return series.fillna(fill_values) 
  

Это решение работает в 1 серии одновременно и может быть вызвано следующим образом:

 out = na_randomfill(df["Apple_cat"])

print(out)
0    cat_1
1    cat_2
2    cat_3
3    cat_3
4    cat_2
5    cat_2
Name: Apple_cat, dtype: object
  

В качестве альтернативы вы можете использовать apply для вызова его в каждом из ваших столбцов. Обратите внимание, что из-за if инструкции в нашей функции нам не нужно заранее указывать столбцы, содержащие null, перед вызовом apply :

 out = df.apply(na_randomfill)

print(out)
   ClientId Apple_cat Region  Price
0        21     cat_1  Reg_A      5
1        15     cat_2  Reg_A      6
2         6     cat_3  Reg_B      7
3        91     cat_3  Reg_A      3
4        45     cat_2  Reg_C      7
5        89     cat_2  Reg_C      6
  

Комментарии:

1. На самом деле для моего фрейма данных с примерно 50 000 строк я получаю следующее сообщение об ошибке : ValueError: Cannot take a larger sample than population when 'replace=False' . Тем не менее, он работает с образцами данных, которые я опубликовал, вы знаете, как я могу исправить эту проблему?

2. Я использовал ‘replace = True’, похоже, он работает, но я не уверен, что он действительно делает

3. Ах, replace=True это правильное решение здесь, я думал, что это опция по умолчанию (по-видимому, нет). При выборке массива (скажем, у вас есть значения ["a", "b", "c"] ) с replace = True помощью, если мы выберем этот массив дважды, мы ["a", "b", "c"] выберем его дважды. Что позволяет нам потенциально рисовать "a" дважды, "b" дважды или "c" дважды. Если replace=False и мы сделали 2 розыгрыша, элемент, нарисованный при первом розыгрыше, удаляется из списка перед повторной выборкой. Итак, если мы нарисуем "a" наш первый розыгрыш, то наш второй розыгрыш ограничен ["b", "c"]

4. Спасибо! хорошо объяснено

Ответ №2:

Трудная часть замены NaN в фрейме данных pandas заключается в том, что метод fillna() заменит все nan на одно и то же число, даже если вы добавите какую-то случайность в вызов.

 import random
df['Apple_cat'].applymap(lambda x: x if not np.isnan(x) else np.random.choice(random.choice(list(x.dropna(axis=0))))
  

Это заменит Nan случайной выборкой из остальной части столбца.
Возможно, вам нужно будет заменить np.nan другим типом nan.

Ответ №3:

Вы можете заполнить недостающие значения на основе распределения вероятностей заполненных строк.

 import numpy as np

df[‘<your_column_name>’] = df[‘<your_column_name>’].fillna(‘TBD’)
possible_values = df[‘<your_column_name>’].value_counts().to_dict()

possible_values.pop(‘TBD’)
total_items = sum(possible_values.keys())
possible_values = [(k,v) for k,v in possible_values.items()]
prob_dist = [i[1]/total_items for i in possible_values]

def fill_missing_values(item):
    if item != ‘TBD’:
        index = np.random.choice(np.arange(len(prob_dist), p=prob_dist)
        return possible_values[index]
    return item

df[‘<your_column_name>’] = df[‘<your_column_name>’].apply(lambda x: fill_missing_values(x))
  

Комментарии:

1. Почему вы используете ‘return’ дважды?

2. если элемент не является TBD, то есть уже заполненное значение, которое вам нужно сохранить как есть.