从pandas.df_dummies

时间:2015-12-30 04:55:06

标签: python pandas

来自具有数字和名义数据的数据框:

>>> 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将分类变量转换为虚拟/指示变量:

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

从df_dummies回到df的最优雅的 back_from_dummies 方式是什么?

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

3 个答案:

答案 0 :(得分:2)

idxmax可以轻松完成。

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 :(得分:1)

首先,分隔列:

$('body').animate({
    scrollTop: $('#address').offset().top
});

这允许您切入每个dummied列的不同帧:

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

现在我们可以使用numpy的argmax:

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

*注意:pandas idxmax返回标签,我们想要这个位置,以便我们可以使用Categoricals。*

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

现在我们可以把它们放在一起:

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]

并放回非dummied列:

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

作为一项功能:

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

答案 2 :(得分:1)

与@David类似,我发现idxmax将为您完成大部分工作。我认为,当你试图将列转换回来时,没有万无一失的方法可以保证你没有问题,因为在某些情况下,确定哪些列是假人而哪些不是假人可能很棘手。我发现使用不太可能偶然发生在您的数据中的分隔符可以大大减轻这种情况。 _通常用于具有多个单词的列名中,因此我使用__(双下划线)作为分隔符;我从未在野外的列名中遇到过这种情况。

另请注意,pd.get_dummies会将所有虚拟列移动到末尾。这意味着您无法获得列的原始顺序。

这是我的方法的一个例子。您可以将虚拟列识别为其中包含sep的虚拟列。我们使用df.filter获取虚拟列组,这将使我们可以使用正则表达式匹配列名称(仅在sep工作之前的名称部分;还有其他方法可以执行此部分)。

rename部分剥离了列名称的开头(例如m__),以便剩下的部分是值。然后idxmax提取其中包含1的列名称。这为我们提供了在原始列之一上撤消pd.get_dummies的数据框;我们将每个列上的pd.get_dummies转换的数据帧与other_cols连接在一起 - 这些列没有" dummified"。

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