본문 바로가기

Computer/Pandas

Onehot 인코딩의 역변환 (inverse transform)

반응형

범주형 데이터 (categorical data)를 수치화하는 방법은 대표적으로 1) 0부터 범주 개수까지 범위의 숫자를 할당하는 nominal 인코딩 (Label encoding), 2) 각 범주마다 1의 값을 가지는 dummy variable을 생성하는 onehot 인코딩 방법이 있습니다. (물론 다른 방법들도 있습니다.) 특히, one-hot 인코딩은 Pandas의 get_dummies 함수를 이용해서 쉽게 수행할 수 있었는데요, 이 함수를 사용할 경우 inverse_transform 함수가 정의되어 있는 scikit-learn 함수와는 달리 변환 이후에 원래 데이터 양식으로 역변환 함수를 따로 만들어줘야 합니다. 


다음과 같은 데이터프레임이 있다고 가정했을 때 get_dummies 함수를 이용하면 prefix_sep에 따라 각 범주의 dummy variable이 생성됩니다. 이 상황에서 원래의 "name", "color" 값으로 되돌릴려면 어떻게 해야 할까요?

fruit = pd.DataFrame({'name':['apple', 'banana', 'cherry', 'durian', 'mango'],
                      'color':['red', 'yellow', 'red', 'green', 'yellow']})
dummy_fruit = pd.get_dummies(fruit, columns=['name', 'color'], prefix_sep='_')
dummy_fruit

일단 데이터프레임의 column을 순회하면서 칼럼명에 prefix_sep ('_')이 포함되면 dummy 변수인 것이므로 prefix_sep 기준으로 스플릿하여 dummy화가 된 칼럼/안된 칼럼으로 구분하는 딕셔너리를 생성할 수 있습니다. 이때, dummy 칼럼의 이름은 prefix_sep 앞부분이 원래 컬럼명이라는 것을 이용합니다.

cols2collapse = {
    item.split(prefix_sep)[0]: (prefix_sep in item) for item in df.columns
}

각 컬럼 별로 처리된 pandas 시리즈를 담을 리스트를 하나 생성하고 딕셔너리를 기반으로 순회하면서 dummy화된 변수에 대해 원래 컬럼명으로 필터링하고 (pandas.DataFrame.filter) 0/1로 되어있는 값 중 1로 된 것이 원래의 값이므로 pandas.DataFrame.idxmax 함수를 이용합니다. 이후에는 결과 시리즈에 대해 이름을 원래 칼럼명으로 바꿔주고 (pandas.rename) 리스트에 추가합니다. Dummy화된 변수가 아니라면 그대로 리스트에 추가합니다.

series_list = []
for col, needs_to_collapse in cols2collapse.items():
    if needs_to_collapse:
        undummified = (
            df.filter(like=col)
            .idxmax(axis=1)
            .apply(lambda x: x.split(prefix_sep, maxsplit=1)[1])
            .rename(col)
        )
        series_list.append(undummified)
    else:
        series_list.append(df[col])

최종적으로 concat 함수를 이용하여 리스트에 담긴 pandas 시리즈를 이어붙이고 리턴합니다. 이를 함수화하면 다음과 같습니다.

def undummify(df, prefix_sep="_"):
    cols2collapse = {
        item.split(prefix_sep)[0]: (prefix_sep in item) for item in df.columns
    }
    series_list = []
    for col, needs_to_collapse in cols2collapse.items():
        if needs_to_collapse:
            undummified = (
                df.filter(like=col)
                .idxmax(axis=1)
                .apply(lambda x: x.split(prefix_sep, maxsplit=1)[1])
                .rename(col)
            )
            series_list.append(undummified)
        else:
            series_list.append(df[col])
    undummified_df = pd.concat(series_list, axis=1)
    return undummified_df
  • Pandas의 filter 함수의 like는 문자열을 입력으로 받아 해당 축 (디폴트는 axis=1)에 입력으로 전달한 문자열을 포함한 인덱스나 컬럼을 필터링합니다.
  • Pandas의 idxmax 함수는 주어진 축에 대하여 큰 값을 가지는 인덱스나 칼럼명을 리턴합니다.
    df = pd.DataFrame({'consumption': [10.51, 103.11, 55.48],
                       'co2_emissions': [37.2, 19.66, 1712]},
                       index=['Pork', 'Wheat Products', 'Beef'])
                       
    >>> df.idxmax()
    consumption     Wheat Products
    co2_emissions             Beef
    dtype: object​

원래 데이터로 잘 돌아왔네요.

반응형