Страницы

Поиск по вопросам

воскресенье, 29 марта 2020 г.

Как эффективно объединить несколько pandas DataFrame?

#python #pandas #numpy #dataframe


Готовлю данные для модели, в частности заполняю пропуски для вещественных признаков
и преобразую категориальные. Исходные данные имеют размерность (40000, 230), из них
первые 190 признаков вещественные, остальные — категориальные.


Подготовим вещественные данные для анализа. Заполним пропуски средним

X_data = pd.DataFrame()
for column in data.columns[:num_of_numerical_vars]:
    m1 = data[column].mean()
    if np.isnan(m1):
        m1 = data[column].notnull().mean()
    X_data[column] = data[column].fillna(m1) 

Подготовим категориальные признаки. Воспользуемся LabelEncoder в 
связке с OneHotEncoder

label_encoder = LabelEncoder()
data_categorical_num = data_categorical.apply(lambda col: label_encoder.fit_transform(col))
hot_encoder = OneHotEncoder(handle_unknown='ignore', sparse=False)
encoded_categorical = hot_encoder.fit_transform(data_categorical_num)



Выводим размерность encoded_categorical

print encoded_categorical.shape


Вывод:


  (40000, 143513)


То есть данных довольно много. Подскажите, как в этом случае объединить получившиеся
вещественные и категориальные признаки эффективно? Если написать что–то вроде:

pd_cats = pd.DataFrame(data=encoded_categorical)
X_data = pd.concat([X_data, pd_cats])


То на моем ПК не хватает памяти для данной операции. Как мне кажется, данные можно
объединить как–то без копирования данных.

Данные могут быть загружены отсюда ("small" dataset)
    


Ответы

Ответ 1



В данном случае стоит использовать разреженные матрицы / фреймы. from sklearn.preprocessing import MultiLabelBinarizer mlb = MultiLabelBinarizer(sparse_output=True) WORK_DIR = Path(r'D:\data\927487') train = pd.read_csv(WORK_DIR / 'orange_small_train.data', sep='\t') labels = pd.read_csv(WORK_DIR / 'orange_small_train_appetency.labels', header=None, squeeze=True, dtype='int8') categorical_cols = train.columns[train.dtypes.eq('object')] train_cat = train[categorical_cols].copy() train = pd.SparseDataFrame(train.drop(categorical_cols, axis=1)) train = train.join( pd.SparseDataFrame( mlb.fit_transform( train_cat.T.apply(lambda c: c.dropna() .str.cat(sep=' ') .split())), default_fill_value=0)) Результат: In [242]: type(train) Out[242]: pandas.core.sparse.frame.SparseDataFrame In [243]: train.memory_usage().sum() Out[243]: 27806864 # NOTE: sparse DF takes only 27 MiB in RAM In [244]: train.shape Out[244]: (50000, 71696)

Ответ 2



Несколько слов об обработке столбцов с преимущественно отсутствующими данными... Если внимательно посмотреть на исходные данные, то можно заметить, что львиная доля (154) столбцов состоит из NaN на 90+%: train = pd.read_csv(WORK_DIR / 'orange_small_train.data', sep='\t') labels = pd.read_csv(WORK_DIR / 'orange_small_train_appetency.labels', header=None, squeeze=True, dtype='int8') оценка количества столбцов состоящих преимущественно из NaN: In [52]: (train.isna().sum() / len(train) > 0.9).sum() Out[52]: 154 мне кажется стоит попробовать построить регрессионную модель в которой данные столбцы не будут учавствовать вовсе и сравнить с той моделью, где такие данные заполнены средним значением (с высокой долей вероятности эти столбцы/признаки не сильно повлияют на результаты предсказываний): In [54]: train = train.loc[:, train.isna().sum() / len(train) <= 0.9] In [55]: train.shape Out[55]: (50000, 76) categorical_cols = train.columns[train.dtypes.eq('object')] train_cat = train[categorical_cols].copy() # drop categorical cols from the train DF train = train.drop(categorical_cols, axis=1) Чтобы заполнить NaN в оставшихся вещественных столбцах можно воспользоваться sklearn.imputer.SimpleImputer. Кроме того стоит нормализовать данные, чтобы все признаки имели одинаковую шкалу величин - для этого существует sklearn.preprocessing.StandardScaler: from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler impute = SimpleImputer(strategy='mean') scale = StandardScaler() train = pd.SparseDataFrame( scale.fit_transform(impute.fit_transform(train)), columns=train.columns, index=train.index, default_fill_value=0) train = train.join( pd.SparseDataFrame( mlb.fit_transform( train_cat.T.apply(lambda c: c.dropna() .str.cat(sep=' ') .split())), default_fill_value=0)) In [82]: train.memory_usage().sum() Out[82]: 21455996 In [83]: train.shape Out[83]: (50000, 71542) In [84]: train.info() RangeIndex: 50000 entries, 0 to 49999 Columns: 71542 entries, Var6 to 71499 dtypes: float64(42), int32(71500) memory usage: 20.5 MB PS также стоит попробовать воспользоваться методом главных компонент (sklearn.decomposition.PCA) для уменьшения размерности (уменьшить число признаков) выборки и сравнить результаты регрессии. PCA позволяет значительно уменьшить число признаков, выбрав самые значимые. Как работает MultilabelBinarizer: In [104]: mlb = MultiLabelBinarizer() In [105]: df = pd.DataFrame({ ...: 'Col1': [10, 20, 30], ...: 'Col2': [['Apple','Orange','Banana'], ['Apple','Grape'], ['Banana']] ...: }) In [106]: df Out[106]: Col1 Col2 0 10 [Apple, Orange, Banana] 1 20 [Apple, Grape] 2 30 [Banana] In [107]: df.join(pd.DataFrame(mlb.fit_transform(df['Col2']), index=df.index, columns=mlb.classes_)) Out[107]: Col1 Col2 Apple Banana Grape Orange 0 10 [Apple, Orange, Banana] 1 1 0 1 1 20 [Apple, Grape] 1 0 1 0 2 30 [Banana] 0 1 0 0

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

Отправить комментарий