

データ分析初心者です。  
初めて簡単なコンペのようなものに挑戦しています。  
何からやればよいのかわからず手探りでググって一つ一つ進めてきたのですが、これについてはどのようにググれば答えが見つかるのかもわからずの状態です。  
そのため、タイトルもなかなかうまく言語化できておらず、わかりにくくなってしまっていると思います。すみません。

コンペの内容としては民泊サービスの価格予測です。物件に関する情報と価格のデータが与えられています。  
中身のデータを精査しているのですがその中で「設備・アメニティ」についてのカラム（'amenities'）がありました。  
こちらのカラムが下記のような形でリスト形式でデータを保持しています。

[code]

    {TV,"Wireless Internet",Kitchen,"Free parking on premises",Washer,Dryer,"Smoke detector"}
    {TV,"Cable TV",Internet,"Wireless Internet","Air conditioning",Kitchen,"Free parking on premises",Heating,Washer,Dryer,"Smoke detector","Carbon monoxide detector","First aid kit",Essentials,Shampoo,"Lock on bedroom door","24-hour check-in",Hangers,Iron,"Laptop friendly workspace","translation missing: en.hosting_amenity_49","translation missing: en.hosting_amenity_50"}
    
[/code]

特段このアメニティの充実度が価格に影響を与えるとは思えにくいのですが、一応確認をしてみたいと思っており、下記のような操作をしたいと思っております。

  * 全データ(約5万レコード)からアメニティに記載されている要素を過不足なく抽出し、その要素分、新たに列を作成する。
  * 新たに作成した列に対してそれぞれ元の'amenities'に値が含まれていた場合は1、そうでない場合は0を設定していく。

pandasでこういった操作ができないか調べてみたのですが全く見当がつかない状態です。  
CSVを直接触れば、1.はできそうなのですが、2.を機械的にやる方法が思いつきません。

質問は2点あります。

  1. 上記を実現する方法はありますか？
  2. こういった1カラムにリストが入っているようなデータを扱うときの一般的な扱い方のルールのようなものがあれば教えてください。


CC BY-SA 4.0
2021-08-03T17:25:31.717
80570
2022-10-21T04:04:35.520
2021-08-04T00:35:13.530
3060
47585
question
python pandas csv
一つのカラムの中にリストでデータが入っている場合、リストの各要素を分割して新たに列を増やし、各要素をダミー変数化方法はありますか？


## 前提

### 準備

下記のような文字列からなるデータシリーズ

[code]

    >>> import pandas as pd
    >>> amenity = pd.Series([
        '{HOGE, "PI YO", HUGA}',
        '{HOGE, "PI YO", HUGA}',
    ])
    >>> amenity
    0    {HOGE, "PI YO", HUGA}
    1    {HOGE, "PI YO", HUGA}
    dtype: object
    
[/code]

を

[code]

    >>> table = str.maketrans("","",'{\"}')
    >>> amenity = amenity.str.translate(table).str.split(",")
    >>> amenity
    0    [HOGE,  PI YO,  HUGA]
    1    [HOGE,  PI YO,  HUGA]
    dtype: object
    
[/code]

により、文字列のリストからなるデータシリーズに変換する。

### 問題設定

以上の準備を行った下で、やりたいこととして下記を仮定します。

下記のようなデータ構造

[code]

    amenity = [
        ["A", "B"],
        ["B", "C"],
        ["A"],
        ["B", "C"],
    ]
    
[/code]

から

[code]

       A  B  C
    0  1  1  0
    1  0  1  1
    2  1  0  0
    3  0  1  1
    
[/code]

のようなデータ構造を作りたい。

## 解決策

私の場合は下記のような操作を行うことが多いです。

[code]

    Python 3.8.11 (default, Aug  3 2021, 06:49:12) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import pandas as pd
    >>> pd.__version__
    >>> pd.__version__     
    '1.3.1'
    >>> df = pd.DataFrame({
        "amenity": [
            {"A", "B"},
            {"B", "C"},
            {"A"},
            {"B", "C"},
        ]
    })  # サンプルデータ作成
    >>> df
    
      amenity
    0  {A, B}
    1  {C, B}
    2     {A}
    3  {C, B}
    
    >>> df.amenity
          .map(lambda s: {k: True for k in s})  # set から 辞書に変換
          .apply(pd.Series)                     # 要素を expand
          .fillna(False)                        # 欠損値を False に
    
           A      B      C
    0   True   True  False
    1  False   True   True
    2   True  False  False
    3  False   True   True
    
[/code]

注意：可読性のため、適当なところで改行やコメントを加えているので、コピペでは実行できませんので、ご注意を。ご自身で必要な修正をお願いします。

## その他の方法

scikit-learn にもこのような処理を行うための Transformer が用意されています。  
pandas と scikit-learn の処理どちらを使用するのが一般的かどうかは判断しかねますが、ご参考までに。

<https://scikit-
learn.org/stable/modules/generated/sklearn.preprocessing.MultiLabelBinarizer.html>

## 類似スレッド

"pandas list column dummy" で検索すると、

<https://stackoverflow.com/questions/29034928/pandas-convert-a-column-of-list-
to-dummies>

がヒットしました。  
こちらでも pandas の pd.Series の apply と MultiLabelBinarizer
による方法がソリューションとして提案されているようです。


CC BY-SA 4.0
2021-08-04T00:04:55.270
80580
2021-08-04T06:13:01.683
2021-08-04T06:13:01.683
47545
47545
80570
answer


Airbnbのデータですかね。

このカラムのそれぞれのデータは、各要素が`,`で区切られています。また「Wireless
Internet」のように2語以上からなるために空白文字を含む要素は、`""`でくくられています。したがって、以下のような手順を踏みます。

  1. データの両端の括弧`{}`を取る。
  2. 要素を括っている`""`を取る。
  3. 区切り`,`に基づいてダミー変数化。

1.と2.は`{}"`の3種の文字を消すという作業になります。Pandasでは`.str.replace()`メソッドで行うことができます。  
3.のように、区切りを含む文字列データをダミー変数化する場合は、Pandasでは`.str.get_dummies()`メソッドを用いるのが容易です。

以下は実行例です。メソッドチェーンにより、正規表現を用いて`{}"`の三種の文字を削除してからダミー変数化するまでを一行で書いています。（データは[このページ](https://www.kaggle.com/airbnb/seattle?select=listings.csv)のものを使いました）

[code]

    In [2]: df = pd.read_csv('listings.csv')
       ...: s = df['amenities']
       ...: s.head()
    Out[2]:
    0    {TV,"Cable TV",Internet,"Wireless Internet","A...
    1    {TV,Internet,"Wireless Internet",Kitchen,"Free...
    2    {TV,"Cable TV",Internet,"Wireless Internet","A...
    3    {Internet,"Wireless Internet",Kitchen,"Indoor ...
    4    {TV,"Cable TV",Internet,"Wireless Internet",Ki...
    Name: amenities, dtype: object
    
    In [3]: s.str.replace(r'{|}|"', "", regex=True).str.get_dummies(',')
    Out[3]:
          24-Hour Check-in  Air Conditioning  Breakfast  ...  Washer / Dryer  Wheelchair Accessible  Wireless Internet
    0                    0                 1          0  ...               0                      0                  1
    1                    0                 0          0  ...               0                      0                  1
    2                    0                 1          0  ...               0                      0                  1
    3                    0                 0          0  ...               0                      0                  1
    4                    0                 0          0  ...               0                      0                  1
    ...                ...               ...        ...  ...             ...                    ...                ...
    3813                 0                 1          0  ...               0                      1                  1
    3814                 1                 0          0  ...               0                      0                  1
    3815                 0                 0          1  ...               0                      0                  1
    3816                 0                 0          0  ...               0                      0                  1
    3817                 0                 0          0  ...               0                      0                  1
    
    [3818 rows x 41 columns]
    
[/code]


CC BY-SA 4.0
2021-08-04T02:33:01.763
80582
2021-08-04T02:33:01.763
37167
80570
answer


[pandas.get_dummies](https://pandas.pydata.org/docs/reference/api/pandas.get_dummies.html#pandas-
get-dummies) を使います。

[code]

    import pandas as pd
    
    amenities = pd.Series([
      '{TV,"Wireless Internet",Kitchen,"Free parking on premises",Washer,Dryer,"Smoke detector"}',
      '{TV,"Cable TV",Internet,"Wireless Internet","Air conditioning",Kitchen,"Free parking on premises",Heating,Washer,Dryer,"Smoke detector","Carbon monoxide detector","First aid kit",Essentials,Shampoo,"Lock on bedroom door","24-hour check-in",Hangers,Iron,"Laptop friendly workspace","translation missing: en.hosting_amenity_49","translation missing: en.hosting_amenity_50"}',
    ])
    
    category = pd.get_dummies(
      amenities.str.strip('{}"')
               .str.split(r'"?,"?', expand=True)
               .stack()
    ).groupby(level=0).sum()
    
    category.T.to_markdown()
    
[/code]

| 0 | 1  
---|---|---  
**24-hour check-in** | 0 | 1  
**Air conditioning** | 0 | 1  
**Cable TV** | 0 | 1  
**Carbon monoxide detector** | 0 | 1  
**Dryer** | 1 | 1  
**Essentials** | 0 | 1  
**First aid kit** | 0 | 1  
**Free parking on premises** | 1 | 1  
**Hangers** | 0 | 1  
**Heating** | 0 | 1  
**Internet** | 0 | 1  
**Iron** | 0 | 1  
**Kitchen** | 1 | 1  
**Laptop friendly workspace** | 0 | 1  
**Lock on bedroom door** | 0 | 1  
**Shampoo** | 0 | 1  
**Smoke detector** | 1 | 1  
**TV** | 1 | 1  
**Washer** | 1 | 1  
**Wireless Internet** | 1 | 1  
**translation missing: en.hosting_amenity_49** | 0 | 1  
**translation missing: en.hosting_amenity_50** | 0 | 1


CC BY-SA 4.0
2021-08-04T21:03:37.283
80598
2021-08-04T21:03:37.283
47127
80570
answer