2015-12-30 15 views
7

Z dataframe z numerycznym i nominalnej danych:Najbardziej elegancki sposób, aby wrócić z pandas.df_dummies

>>> from pandas import pd 
>>> d = {'m': {0: 'M1', 1: 'M2', 2: 'M7', 3: 'M1', 4: 'M2', 5: 'M1'}, 
     'qj': {0: 'q23', 1: 'q4', 2: 'q9', 3: 'q23', 4: 'q23', 5: 'q9'}, 
     'Budget': {0: 39, 1: 15, 2: 13, 3: 53, 4: 82, 5: 70}} 
>>> df = pd.DataFrame.from_dict(d) 
>>> df 
    Budget m qj 
0  39 M1 q23 
1  15 M2 q4 
2  13 M7 q9 
3  53 M1 q23 
4  82 M2 q23 
5  70 M1 q9 

get_dummies przekonwertować zmienną kategoryczne do manekina zmiennych/wskaźnik:

>>> df_dummies = pd.get_dummies(df) 
>>> df_dummies 
    Budget m_M1 m_M2 m_M7 qj_q23 qj_q4 qj_q9 
0  39  1  0  0  1  0  0 
1  15  0  1  0  0  1  0 
2  13  0  0  1  0  0  1 
3  53  1  0  0  1  0  0 
4  82  0  1  0  1  0  0 
5  70  1  0  0  0  0  1 

Co najbardziej elegancki back_from_dummies sposób na powrót z df_dummies do df?

>>> (back_from_dummies(df_dummies) == df).all() 
Budget True 
m   True 
qj  True 
dtype: bool 
+0

Powrót do df? Nie jestem pewien, co masz na myśli. –

+0

Podam tylko zwrot/cofnięcie – user3313834

+0

Dzięki. Chciałem tylko upewnić się. –

Odpowiedz

1

idxmax zrobi to całkiem łatwo.

from itertools import groupby 

def back_from_dummies(df): 
    result_series = {} 

    # Find dummy columns and build pairs (category, category_value) 
    dummmy_tuples = [(col.split("_")[0],col) for col in df.columns if "_" in col] 

    # Find non-dummy columns that do not have a _ 
    non_dummy_cols = [col for col in df.columns if "_" not in col] 

    # For each category column group use idxmax to find the value. 
    for dummy, cols in groupby(dummmy_tuples, lambda item: item[0]): 

     #Select columns for each category 
     dummy_df = df[[col[1] for col in cols]] 

     # Find max value among columns 
     max_columns = dummy_df.idxmax(axis=1) 

     # Remove category_ prefix 
     result_series[dummy] = max_columns.apply(lambda item: item.split("_")[1]) 

    # Copy non-dummy columns over. 
    for col in non_dummy_cols: 
     result_series[col] = df[col] 

    # Return dataframe of the resulting series 
    return pd.DataFrame(result_series) 

(back_from_dummies(df_dummies) == df).all() 
1

Po pierwsze, oddzielne kolumny:

In [11]: from collections import defaultdict 
     pos = defaultdict(list) 
     vals = defaultdict(list) 

In [12]: for i, c in enumerate(df_dummies.columns): 
      if "_" in c: 
       k, v = c.split("_", 1) 
       pos[k].append(i) 
       vals[k].append(v) 
      else: 
       pos["_"].append(i) 

In [13]: pos 
Out[13]: defaultdict(list, {'_': [0], 'm': [1, 2, 3], 'qj': [4, 5, 6]}) 

In [14]: vals 
Out[14]: defaultdict(list, {'m': ['M1', 'M2', 'M7'], 'qj': ['q23', 'q4', 'q9']}) 

To pozwala pokroić w różnych klatek dla każdej kolumny dummied:

In [15]: df_dummies.iloc[:, pos["m"]] 
Out[15]: 
    m_M1 m_M2 m_M7 
0  1  0  0 
1  0  1  0 
2  0  0  1 
3  1  0  0 
4  0  1  0 
5  1  0  0 

Teraz możemy wykorzystać argmax NumPy za:

In [16]: np.argmax(df_dummies.iloc[:, pos["m"]].values, axis=1) 
Out[16]: array([0, 1, 2, 0, 1, 0]) 

* Uwaga: patelnia das idxmax zwraca się etykietę chcemy pozycję, dzięki czemu możemy użyć Categoricals *

In [17]: pd.Categorical.from_codes(np.argmax(df_dummies.iloc[:, pos["m"]].values, axis=1), vals["m"]) 
Out[17]: 
[M1, M2, M7, M1, M2, M1] 
Categories (3, object): [M1, M2, M7] 

Teraz możemy umieścić to wszystko razem.

In [21]: df = pd.DataFrame({k: pd.Categorical.from_codes(np.argmax(df_dummies.iloc[:, pos[k]].values, axis=1), vals[k]) for k in vals}) 

In [22]: df 
Out[22]: 
    m qj 
0 M1 q23 
1 M2 q4 
2 M7 q9 
3 M1 q23 
4 M2 q23 
5 M1 q9 

i oddanie z powrotem non-dummied kolumny :

In [23]: df[df_dummies.columns[pos["_"]]] = df_dummies.iloc[:, pos["_"]] 

In [24]: df 
Out[24]: 
    m qj Budget 
0 M1 q23  39 
1 M2 q4  15 
2 M7 q9  13 
3 M1 q23  53 
4 M2 q23  82 
5 M1 q9  70 

Jako funkcja:

def reverse_dummy(df_dummies): 
    pos = defaultdict(list) 
    vals = defaultdict(list) 

    for i, c in enumerate(df_dummies.columns): 
     if "_" in c: 
      k, v = c.split("_", 1) 
      pos[k].append(i) 
      vals[k].append(v) 
     else: 
      pos["_"].append(i) 

    df = pd.DataFrame({k: pd.Categorical.from_codes(
           np.argmax(df_dummies.iloc[:, pos[k]].values, axis=1), 
           vals[k]) 
         for k in vals}) 

    df[df_dummies.columns[pos["_"]]] = df_dummies.iloc[:, pos["_"]] 
    return df 

In [31]: reverse_dummy(df_dummies) 
Out[31]: 
    m qj Budget 
0 M1 q23  39 
1 M2 q4  15 
2 M7 q9  13 
3 M1 q23  53 
4 M2 q23  82 
5 M1 q9  70 
0

Podobny do @David, stwierdzam, że większość pracy wykona użytkownik idxmax. Myślę, że nie ma niezawodny sposób, aby zagwarantować, że nie masz problemów, gdy próbujesz przekonwertować kolumny z powrotem, jednak, ponieważ w niektórych przypadkach może być trudne do określenia, które kolumny są manekiny, a które nie. Uważam, że można to znacznie złagodzić poprzez użycie separatora, który jest bardzo mało prawdopodobny w danych przez przypadek. _ jest często używany w nazwach kolumn zawierających wiele słów, więc używam __ (podwójny podkreślnik) jako separatora; Nigdy nie natknąłem się na to w nazwie kolumny na wolności.

Należy również pamiętać, że pd.get_dummies przesunie wszystkie kolumny pozorne na koniec. Oznacza to, że niekoniecznie możesz odzyskać oryginalną kolejność kolumn.

Oto przykład mojego podejścia. Możesz rozpoznać atrapę kolumn jako te z sep w nich. Otrzymujemy grupy kolumn fikcyjnych za pomocą df.filter, które pozwalają nam dopasowywać nazwy kolumn za pomocą wyrażeń regularnych (tylko część nazwy przed wykonaniem sep działa, są inne sposoby na wykonanie tej części).

Część rename odcina początek nazw kolumn (np.m__), tak aby pozostałą częścią była wartość. Następnie idxmax wyodrębnia nazwę kolumny, która ma w sobie 1. To daje nam ramkę danych z cofania pd.get_dummies na jednej z oryginalnych kolumn; łączymy razem ramki danych z odwracaniem pd.get_dummies na każdej z kolumn wraz z other_cols - kolumnami, które nie zostały "zmumifikowane".

In [1]: import pandas as pd 

In [2]: df = pd.DataFrame.from_dict({'m': {0: 'M1', 1: 'M2', 2: 'M7', 3: 'M1', 4: 'M2', 5: 'M1'}, 
    ...:   'qj': {0: 'q23', 1: 'q4', 2: 'q9', 3: 'q23', 4: 'q23', 5: 'q9'}, 
    ...:   'Budget': {0: 39, 1: 15, 2: 13, 3: 53, 4: 82, 5: 70}}) 

In [3]: df 
Out[3]: 
    Budget m qj 
0  39 M1 q23 
1  15 M2 q4 
2  13 M7 q9 
3  53 M1 q23 
4  82 M2 q23 
5  70 M1 q9 

In [4]: sep = '__' 

In [5]: dummies = pd.get_dummies(df, prefix_sep=sep) 

In [6]: dummies 
Out[6]: 
    Budget m__M1 m__M2 m__M7 qj__q23 qj__q4 qj__q9 
0  39  1  0  0  1  0  0 
1  15  0  1  0  0  1  0 
2  13  0  0  1  0  0  1 
3  53  1  0  0  1  0  0 
4  82  0  1  0  1  0  0 
5  70  1  0  0  0  0  1 

In [7]: dfs = [] 
    ...: 
    ...: dummy_cols = list(set(col.split(sep)[0] for col in dummies.columns if sep in col)) 
    ...: other_cols = [col for col in dummies.columns if sep not in col] 
    ...: 
    ...: for col in dummy_cols: 
    ...:  dfs.append(dummies.filter(regex=col).rename(columns=lambda name: name.split(sep)[1]).idxmax(axis=1)) 
    ...: 
    ...: df = pd.concat(dfs + [dummies[other_cols]], axis=1) 
    ...: df.columns = dummy_cols + other_cols 
    ...: df 
    ...: 
Out[7]: 
    qj m Budget 
0 q23 M1  39 
1 q4 M2  15 
2 q9 M7  13 
3 q23 M1  53 
4 q23 M2  82 
5 q9 M1  70 
Powiązane problemy