質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.37%

pandasを高速化したいです

解決済

回答 4

投稿

  • 評価
  • クリップ 2
  • VIEW 3,203

score 353

import pandas as pd    
df = pd.DataFrame()         
df["column"] = [["a", "c", "e"], ["b"], ["c", "e"], ["a", "e"], ["a", "b", "c"]]    
df.head()   

>>       column
0  [a, c, e]
1        [b]
2     [c, e]
3     [a, e]
4  [a, b, c]     


のようなデータフレームがあるとします。

<やりたいこと>
columnの取りうる値全てに対して、それぞれ各行に対してバイナリーの変数に変換したいです。
つまり

a b c d e
1 0 1 0 1
0 1 0 0 0
0 0 1 0 1
1 0 0 0 1
1 1 1 0 0

を得たいです。

<試したこと>
uniqは取りうる値を格納しています。

for col in uniq:
    df[col] = 0

    for i in range(len(df)):
        if col in df.loc[i, "column"]:
            df.loc[i, col] = 1 
        else:
            df.loc[i, col] = 0

<解決したいこと>

実際に扱っているデータフレームは三万行くらいで、取りうる値は10個くらいであり、
かなり遅いです。
実際にはこの操作を複数回行う必要があり、上の操作を高速化する必要があります。
お力添えいただけないでしょうか?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+2

ループなし、ワンライナーで書いてみました。
やってることは

  1. リストをセルに分割
  2. stack() で縦に並べ替え
  3. pandas.get_dummies()でOne-Hot化
  4. 元のIndex番号同志で結合(sum()

です

import pandas as pd
df = pd.DataFrame({"column":[["a", "c", "e"], ["b"], ["c", "e"], ["a", "e"], ["a", "b", "c"]]})
ret = pd.get_dummies(df['column'].apply(pd.Series).stack()).groupby(level=0).apply(sum)
#   a  b  c  e
#0  1  0  1  1
#1  0  1  0  0
#2  0  0  1  1
#3  1  0  0  1
#4  1  1  1  0

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/09/28 07:59 編集

    このやり方の方が圧倒的に早いですね!
    何万行あってもすぐに終わるのではないでしょうか、、

    キャンセル

  • 2019/09/29 14:50

    10秒ほどでいけました。ありがとうございました〜

    キャンセル

+2

コードは汚いですがcolumnの各文字列を数値化してnumpyでフラグ配列化すると速いです。
参考:Convert integer to binary array with suitable padding

import pandas as pd
import random
import numpy as np

# テストデータ
CHARS = 'abcdefghij' # 'abc'など
N = 30000 # 5など
lst = []
for i in range(N):
    lst.append(random.sample(CHARS,random.randint(1,len(CHARS))))
df = pd.DataFrame({"column":lst})

# テストデータ
df = pd.DataFrame({"column":[["a", "c", "e"], ["b"], ["c", "e"], ["a", "e"], ["a", "b", "c"]]})
print(df)

#
# ['b','a','c] -> 1+2+4 = 7
# ['a'] -> 2 = 2
# のように文字列の出現順に数値化
#
txt2bin = {} # 'a'-> 2 など
bin = 1
def f(l):
    global bin
    n = 0
    for c in l:
        if c not in txt2bin:
            txt2bin[c] = bin
            bin <<= 1
        n += txt2bin[c]
    return n

a = df['column'].apply(f).values

# 数値->フラグ配列化
b = (((a[:,None] & (1 << np.arange(len(txt2bin))))) > 0).astype(int)

# フラグ配列の並び順に列名を並べる
columns = sorted(txt2bin.items(), key=lambda e:e[1])
columns = [ v[0] for v in columns]

ret = pd.DataFrame(b, columns=columns)

# 列名を昇順に
ret = ret.loc[:,sorted(columns)]
print(ret)
"""
   a  b  c  e
0  1  0  1  1
1  0  1  0  0
2  0  0  1  1
3  1  0  0  1
4  1  1  1  0
"""

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/09/28 16:17

    きっと早いのだと思いますが、あとあと修正するかもしれないことを考えると、この方法を使用する決断はしにくいですね。
    でも非常に勉強になります。

    キャンセル

  • 2019/09/28 17:17

    実際に速いです。修正内容にもよるかと思うので採用できないのは残念ですが
    100万行でもほぼ一瞬なので速度面ではメリットあるかと思います。

    キャンセル

  • 2019/09/28 17:36

    本当に速度が重要なシステムだったら、多少可読性を犠牲にしてでもこういった方法を使用した方がいいのでしょうね。

    ベストアンサーの回答も相当早そうなので、もし必要になったら実際の要件に従って実装して、速度比較ですね。

    キャンセル

+1

dataframeをfor文の中で使用すると死ぬほど遅いです。
吐き気がするほどに。

ですので、どうしてもloop処理したいのであれば、辞書型などに変換してしまうのがおすすめです。
(可読性は下がりますが...)

import pandas as pd

# データの準備
df = pd.DataFrame()         
df["column"] = [["a", "c", "e"], ["b"], ["c", "e"], ["a", "e"], ["a", "b", "c"]]    
uniq = ['a', 'b', 'c', 'd', 'e']

# メインのloop処理
dict_results = []
for items in df.to_dict()['column'].values():
    tmp_dict_result = {'a': 0, 'b': 0, 'c': 0, 'd': 0, 'e': 0}
    for col in uniq:
        if col in items:
            tmp_dict_result[col] = 1 
    dict_results.append(tmp_dict_result)

pd.DataFrame.from_dict(dict_results)

ただし、numpy配列化すればベクトル演算ができるので、もっと高速にできるかもしれません。
誰か知ってませんかね....(私も知りたいです)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

バイナリが良くわからないので会っているか分かりませんが

df.astype(bytes)


型の変換だけならこうだと思います

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/09/27 20:28

    すみません、型の変換ではなく、元の変数を上にあるように、0,1の変数に変換したいということです。文脈としては機械学習の前処理になります。

    キャンセル

  • 2019/09/27 20:32

    df.astype(bool)
    df.astype(int)
    多分違いますが
    これでも01にできますよ

    キャンセル

  • 2019/09/27 20:38

    ありがとうございます。上の例のように、元の変数がリストなのです。。。

    キャンセル

  • 2019/09/27 20:58

    そうですか
    理解不足ですいません

    あまり扱わない形式なので
    わかんないかもです

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.37%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る