Панды и флинна по группам - PullRequest
1 голос
/ 15 мая 2019

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

У меня есть DataFrame, который содержит набор данных для последующего машинного обучения. Имеет столбцы объектов (около 500) и 4 столбца целей. Цели связаны друг с другом в возрастающей степени детализации (например, ошибка / отсутствие_файла, ошибка-где, группа ошибок, ошибка-точная). DataFrame имеет довольно много значений NaN, так как он был скомпилирован из 2 отдельных наборов данных с помощью соединения OUTER - некоторые строки заполнены, другие содержат данные из одного набора данных, но не из другого и т. Д. - см. Рисунок ниже, и извините за ужасный редактирует.

enter image description here

В любом случае, Transformer от Sci-kit Learn SimpleImputer () не дал мне результатов ML, к которым я стремился, и я подумал, что, возможно, мне следует провести вменение на основе целей, как, например, в. вычислите среднее значение для выборок, доступных для каждой цели в каждом столбце, и рассчитайте их. Затем проверьте, если остались какие-либо значения NaN, и, если они есть, перейдите к tar_3 (на один уровень детализации вниз), вычислите также медиану и рассчитайте это значение для каждой цели, для каждого столбца. И так до тех пор, пока не останется ни одного NaN.

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

tar_list = ['tar_4', 'tar_3', 'tar_2', 'tar_1']

for tar in tar_list:

    medians = df.groupby(by = tar).agg('median')
    print("\nFilling values based on {} column granularity.".format(tar))

    for col in [col for col in df.columns if col not in tar_list]:

        print(col)
        uniques = sorted(df[tar].unique())

        for class_name in uniques:

            value_to_fill = medians.loc[class_name][col]
            print("Setting NaNs for target {} in column {} to {}".format(class_name, col, value_to_fill))
            df.loc[df[tar] == class_name, col] = df.loc[df[tar] == class_name, col].fillna(value = value_to_fill)
    print()

Хотя я доволен результатом, который дает этот код, у него есть 2 недостатка, которые я не могу игнорировать: 1) Требуется вечность, чтобы выполнить даже на моем маленьком наборе данных ~ 1000 выборок x ~ 500 столбцов. 2) Он вменяет одно и то же медианное значение для всех NaN в каждом столбце для целевого значения, над которым он в настоящее время работает. Я бы предпочел, чтобы он вменял что-то с небольшим шумом, чтобы предотвратить просто повторение данных (может быть, либо значение, случайно выбранное из нормального распределения значений в этом столбце для этой цели?).

Насколько мне известно, в Sci-Kit Learn или Pandas нет готовых инструментов для более эффективного решения этой задачи. Однако, если есть - может кто-то направить меня в правильном направлении? Кроме того, я открыт для предложений о том, как улучшить этот код для решения обеих моих проблем.

UPDATE:

Пример генерирования кода, о котором я упоминал:

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')

1 Ответ

1 голос
/ 15 мая 2019

Попробуйте это:

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')
    df.set_index(tar, inplace=True)
    for col in cols:
        df[col] = df[col].fillna(medians[col])
    df.reset_index(inplace=True)

df.index = idx

Взял около 1,5 с с данными образца:

np.random.seed(2019)
len_df=1000
num_cols = 500
df = pd.DataFrame(np.random.choice(list(range(10))+[np.nan],
                                   size=(len_df, num_cols),
                                   p=[0.05]*10+[0.5]),
                  columns=[str(x) for x in range(num_cols)])

for i in range(1,5):
    np.random.seed(i)
    df[f'tar_{i}'] = np.random.randint(i*4, (i+1)*4, len_df)
...