Для начала вот некоторые искусственные данные, подходящие для моей проблемы:
df = pd.DataFrame(np.random.randint(0, 100, size=(vsize, 10)),
columns = ["col_{}".format(x) for x in range(10)],
index = range(0, vsize * 3, 3))
df_2 = pd.DataFrame(np.random.randint(0,100,size=(vsize, 10)),
columns = ["col_{}".format(x) for x in range(10, 20, 1)],
index = range(0, vsize * 2, 2))
df = df.merge(df_2, left_index = True, right_index = True, how = 'outer')
df_tar = pd.DataFrame({"tar_1": [np.random.randint(0, 2) for x in range(vsize * 3)],
"tar_2": [np.random.randint(0, 4) for x in range(vsize * 3)],
"tar_3": [np.random.randint(0, 8) for x in range(vsize * 3)],
"tar_4": [np.random.randint(0, 16) for x in range(vsize * 3)]})
df = df.merge(df_tar, left_index = True, right_index = True, how = 'inner')
Теперь я хотел бы заполнить значения NaN в каждом столбце значением MEDIAN значений, отличных от NaN, в каждом столбце,но с шумом, добавленным к каждому заполненному NaN в этом столбце .Значение MEDIAN должно быть рассчитано для значений в этом столбце, которые принадлежат к тому же классу, который сначала был отмечен в столбце tar_4.Затем, если в столбце сохраняются какие-либо NaN (поскольку некоторые значения в столбце были все в классе tar_4, который содержал только NaN, поэтому нельзя было рассчитать MEDIAN), та же операция повторяется для обновленного столбца (с некоторыми уже заполненными NaN.из операции tar_4), но со значениями, принадлежащими к тому же классу относительно столбца tar_3.Затем tar_2 и tar_1.
Способ, которым я представляю, будет следующим:
- col_1 имеет, например, 6 значений, отличных от Nan и 4 NaN: [1, 2, NaN, 4, NaN, 12,5, NaN, 1, NaN]
- только значения [1, 2, NaN, 4, NaN] относятся к одному и тому же классу (например, класс 1) в tar_4, поэтому они проталкиваются через заполнение NaN:
- Значение NaN в индексе [2] заполняется MEDIAN (= 2) + случайная (-3, 3) * стандартная ошибка распределения в col_1, например, 2 + (1 * 1.24)
- NaNзначение по индексу [4] заполняется MEDIAN (= 2) + случайная (-3, 3) * стандартная ошибка распределения в col_1, например, 2 + (-2 * 1,24)
- Теперь col_1 имеет следующие 8 значений, отличных от NaN и 2 NaN: [1, 2, 1.24, 4, -0.48, 12, 5, NaN, 1, NaN]
- В столбце col_1 все еще присутствуют некоторые значения NaN, поэтому применяется группировка по общему классу в столбце tar_3:
- из [1, 2, 1.24, 4, -0.48, 12, 5, NaN, 1, NaN], значения [1, 2,1.24, 4, -0.48, 12, 5, NaN] теперь в одном классе, поэтому они обрабатываются:
- Значение NaN по индексу [7] gets присваивается МЕДИАНА значений в индексах [0-6] (= 2) + случайная (-3, 3) * стандартная ошибка, например, 2 + 2 * 3,86
- , теперь col_1 имеет 9значения, отличные от NaN, и значение 1 NaN: [1, 2, 1.24, 4, -0.48, 12, 5, 9.72, 1, NaN]
- все значения в col_1 принадлежат одному и тому же классу на основе столбца tar_2таким образом, значение NaN в индексе [9] обрабатывается по той же логике, что и описанная выше, и заканчивается значением 2 * (-1 * 4.05)
- col_1 теперь имеет только не-NaN значения: [1, 2, 1,24, 4, -0,48, 12, 5, 9,72, 1, -6,09], и нет необходимости проходить через заполнение NaN на основе столбца tar_1.
Та же логика распространяется и на остальные столбцы.
Итак, ожидаемый вывод : DataFrame с заполненными значениями NaN, в каждом столбце на основе уменьшения уровня детализации классов на основестолбцы tar_4 - tar_1.
У меня уже есть код, который позволяет добиться этого благодаря @Quang Hoang:
def min_max_check(col):
if ((df[col].dropna() >= 0) & (df[col].dropna() <= 1.0)).all():
return medians[col]
elif (df[col].dropna() >= 0).all():
return medians[col] + round(np.random.randint(low = 0, high = 3) * stds[col], 2)
else:
return medians[col] + round(np.random.randint(low = -3, high = 3) * stds[col], 2)
tar_list = ['tar_4', 'tar_3', 'tar_2', 'tar_1']
cols = [col for col in df.columns if col not in tar_list]
# since your dataframe may not have continuous index
idx = df.index
for tar in tar_list:
medians = df[cols].groupby(by = df[tar]).agg('median')
std = df[cols].groupby(by = df[tar]).agg(np.std)
df.set_index(tar, inplace=True)
for col in cols:
df[col] = df[col].fillna(min_max_check(col))
df.reset_index(inplace=True)
df.index = idx
Однако это заполняет только значения NaNс тем же значением MEDIAN + шум на каждом уровне детализации.Как можно улучшить этот код, чтобы генерировать различные значения заполнения для каждого значения NaN, например, на уровнях tar_4, tar_3, tar_2 и tar_1?