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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

Q&A

解決済

2回答

1853閲覧

pandasで値がTrueの時に、対応する列ラベルを取り出したい。短時間で実行するにはどうしたらよいか。

tecton00

総合スコア8

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

0グッド

1クリップ

投稿2017/11/03 12:24

###前提・実現したいこと
以下のようなブーリアン型の表があるときに、Trueの値がある列のラベルを取り出してまとめたいです。

処理前の表
|product|key1|key2|key3|
|:--|:--:|--:|
|ProductA|True|False|False|
|ProductB|False|Ture|False|
|ProductC|True|False|True|

処理後の表
|product|key1|key2|key3|keys|
|:--|:--:|--:|--:|
|ProductA|True|False|False|key1|
|ProductB|False|Ture|False|key2|
|ProductC|True|False|True|key1,key3|

###発生している問題・エラーメッセージ
以下のようなfor文を使ったコードで目的にあったリストを作ることができたのですが、大きなデータフレーム(400列、15万行)に適用すると実行に数時間かかってしまいます。もっと短時間で実行できるような書き方はできないでしょうか?

###該当のソースコード

Python

1import pandas as pd 2import numpy as np 3 4df = pd.DataFrame([ 5 ["ProductA", True, False, False], 6 ["ProductB", False, True, False], 7 ["ProductC", True, False, True]], 8 columns=['product', 'key1', 'key2', 'key3']) 9 10keys = [] 11for col in range(0,len(df.index)): 12 key = [] 13 for row in range(1,len(df.columns)): 14 if df.iloc[[col],[row]].values: 15 key.append(df.columns[row]) 16 keys.append(key) 17keys

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

pandasで書くならこんな感じですかね。

python

1import pandas as pd 2 3# 行ごとのデータを受け取って、Trueの列だけのindexをリストに変換してjoinするlambda関数を定義 4f = lambda series: ",".join(series.index[series==True].tolist()) 5 6# 入出力例 7f(df.iloc[2]) 8# >'key1,key3' 9 10# applyでaxis=1(行方向に演算)でfuncを適用する 11# 結果をdfのkeys列に新規に挿入 12df['keys'] = df.apply(f, axis = 1) 13 14df 15# > product key1 key2 key3 keys 16# > 0 ProductA True False False key1 17# > 1 ProductB False True False key2 18# > 2 ProductC True False True key1,key3 19

無理にlambdaを使わなくても普通の関数でもOKです。

python

1def func(series): 2 columnNames=series.index[series==True].tolist() 3 return ",".join(columnNames) 4 5df['keys'] = df.apply(func, axis = 1)

投稿2017/11/06 04:00

編集2017/11/09 23:42
y__sama

総合スコア83

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tecton00

2017/11/07 14:10

ご教示ありがとうございます。 おかげさまで実際のデータでも短時間で実行することができました。 lambda関数は今まで避けてきたのですが、きちんと勉強しようと思います。
guest

0

ベストアンサー

以下が時間消費の主因になっていると考えます。
0. forループを二重に回しているところ。
0. ループの最奥でappendを一々実行しているところ。

これを解消するため以下の点に留意してスクリプト後半を書きかえてみました。
0. ループの外に出せる処理は全て外に出す。
0. Trueである列の列番号を行毎に拾う処理をリスト内包表記で記述して、forループを一つ消す。
0. 拾った列番号は別リスト(df1)に格納。それ以外の処理はループ内では一切行わない。

python

1df1 = list(np.repeat("",len(df.index))) 2l = len(df.ix[0]) 3cols = list(df) 4 5for row in range(0,len(df.index)): 6 df1[row] = [x[0] for x in enumerate(df.ix[row, range(1,l)]) if x[1]] 7 8# とりあえず第2行目の結果をそれっぽく出力してみる。 9print df.ix[1,0]+"\t", "\t".join( [cols[x+1] for x in df1[1]] ) 10

5000行 x 400列のデータを処理してみたところ、質問文にあるスクリプトでは 1280秒掛かった処理が、上記改変版では7秒で終わりました。

投稿2017/11/03 18:38

編集2017/11/05 03:59
KojiDoi

総合スコア13671

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tecton00

2017/11/04 20:45

ご教示ありがとうございました! 私の環境でも数時間かかっていた処理が数十秒で実行できました。 考え方も教えていただきありがとうございます。データ量が多くなるとコードの書き方ひとつで処理能力が大きく変わることを実感できました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問