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

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

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

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

732閲覧

Pythonの計算速度アップ

Hidd

総合スコア11

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2017/08/29 17:19

編集2017/08/30 17:47

初心者です。Pythonを使って以下のようなForループを使って計算をしているのですが、データが大量にあるので計算に非常に時間がかかります。
この計算を早くするにはどのようにコードを変えれば良いでしょうか。もっと上手い計算方法があるような気がするのですが。。
よろしくお願いします。

Python

1for (wcolumn, zcolumn) in zip(W_columns, Z_columns): 2 afterdf2[wcolumn] = 0.0 3 for date1 in dates: 4 for name1 in sector_names: 5 z20sum = afterdf2[(afterdf2.DATE == date1) & (afterdf2.SECTOR == name1) & (afterdf2[zcolumn] >= np.percentile(afterdf2[(afterdf2.DATE == date1) & (afterdf2.SECTOR == name1)].ix[:, zcolumn],80))].ix[:,zcolumn].sum() 6 afterdf2[wcolumn] = np.where((afterdf2.DATE == date1) & (afterdf2.SECTOR == name1) & (afterdf2[zcolumn] >= np.percentile(afterdf2[(afterdf2.DATE == date1) & (afterdf2.SECTOR == name1)].ix[:, zcolumn],80)), afterdf2[zcolumn] / z20sum * afterdf2['Sectorweight'] , afterdf2[wcolumn] ) 7

式がややこしいですが、dates, sector_namesの要素ごとに、zcolumnが上位2割の数値を取り出して計算してafterdf2[wcolumn]というところに数値を返しています。

【上記と似たコードですが追加で質問です】
上記については、教えて頂いた通りにgroupby, transform, np.whereを組み合わせることで計算時間が数十分の1に改善しました。ありがとうございます。
他のコードも同様に修正できたのですが、どうしても以下のコードが修正できません。上記のコードとほぼ一緒で、DATE, SECTORでgroupby()をするというところまでは同じだと思うのですが、zcolumnの上位20%の行のMKT_CAPという列の数字を操作するところが違い上手くいきません。

Python

1for (wcolumn, zcolumn) in zip(WM_columns, Z_columns): 2 afterdf2[wcolumn] = 0.0 3 for date1 in dates: 4 for name1 in sector_names: 5 z20sum = afterdf2[(afterdf2.DATE == date1) & (afterdf2.SECTOR == name1) & (afterdf2[zcolumn] >= np.percentile(afterdf2[(afterdf2.DATE == date1) & (afterdf2.SECTOR == name1)].ix[:, zcolumn],80))].ix[:,'MKT_CAP'].sum() 6 afterdf2[wcolumn] = np.where((afterdf2.DATE == date1) & (afterdf2.SECTOR == name1) & (afterdf2[zcolumn] >= np.percentile(afterdf2[(afterdf2.DATE == date1) & (afterdf2.SECTOR == name1)].ix[:, zcolumn],80)), afterdf2['MKT_CAP'] / z20sum * afterdf2['Sectorweight'] , afterdf2[wcolumn] )

教えて頂いたコードを参考に以下のように直してみたのですが、文法的に間違っているのか、TypeError: Transform function invalid for data typesというエラーになります。もし解決法がお分かりでしたら教えて頂けると大変助かります。よろしくお願いします。

Python

1afterdf2.groupby(['DATE','SECTOR'])[[zcolumn,'MKT_CAP']].transform(lambda d,m : np.where(d >= d.quantile(0.8), m / m[d >= d.quantile(0.8)].sum(), 0))

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2017/08/29 17:35

ソースはコードブロックで囲まないと、pythonの場合正しく読めないので、修正してください
guest

回答2

0

ベストアンサー

とりあえず、datessector_names のループは DataFrame.groupby() を使用することでシンプルに記述することが出来ます。

DATESECTORの組み合わせでgroupby()にてグループを作成し、そのグループごとのzcolumnを使うことで計算を行います。

また、zcolumnの上位20%はpandasでは、Series.quantile()が使用できます。

質問に挙げているコードを、上記の方法で書き換えるとこんな感じになります。

Python

1for (wcolumn, zcolumn) in zip(W_columns, Z_columns): 2 afterdf2[wcolumn] = afterdf2.groupby(['DATE','SECTOR'])[zcolumn].transform(lambda d : np.where(d >= d.quantile(0.8),d / d[d >= d.quantile(0.8)].sum(), 0)) 3 afterdf2[wcolumn] = afterdf2[wcolumn] * afterdf2['Sectorweight']

この記述で、単にループで計算するよりはかなり高速になるかと思います。


【補足】
別の列を処理するサンプル

Python

1for (wcolumn, zcolumn) in zip(W_columns, Z_columns): 2 # 上記20%以内かどうかのBoolean列を作成 3 z20_filter = afterdf2.groupby(['DATE','SECTOR'])[zcolumn].transform(lambda d:d >= d.quantile(0.8)) 4 # 上記のBoolean列にてFilteringした後に"MKT_CAP"列を計算 5 afterdf2['PP'] = afterdf2[z20_filter].groupby(['DATE', 'SECTOR'])['MKT_CAP'].transform(lambda d:d/d.sum()) 6 # 上記のFilterに漏れた場所は値が入っていないので、0埋めする 7 afterdf2['PP'].fillna(0, inplace=True)

投稿2017/08/30 15:05

編集2017/08/31 04:20
magichan

総合スコア15898

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

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

Hidd

2017/08/30 17:49 編集

ありがとうございます。自分で色々考えてもわからなかったのですが、groupby, transform, np.whereを組み合わせれば式が一行で書けるんですね。大変勉強になりました。コードを直したところ計算時間が数十分の1に短縮されました。 他のところも同じようにコードを修正することで、かなり計算時間が短縮されたのですが、ほぼ同じコードなのですがどうしても1箇所修正できないところがあります。質問のところに追記させて頂いているのですが、もし解決法がお分かりでしたら教えて頂けないでしょうか。お手数おかけしますがよろしくお願い致します。
magichan

2017/08/31 04:23 編集

残念ながら、GroupBy.transform() は複数のカラム間での処理には向いておりません。 (GroupBy.apply() を使用すると上手く処理できるかもしれませんが・・あまり良いアイデアが浮かびません。) そのような場合は、先に "zcolumn" にて上位80%以内か否かを示す Boolean列(Series) を作成しておいて、それを使用して、Filtering した後に groupby で "MKT_CAP"列を処理したらよいのではないでしょうか サンプルを回等に追記しました
Hidd

2017/08/31 14:39

ありがとうございます。こんな上手いやり方があるのですね、大変勉強になりました。ご丁寧に色々と教えて頂き本当にありがとうございました。もっとPython的なコードが書けるように自分でも勉強します。
guest

0

afterdf2はpandas型であってますでしょうか?

pandasのdataframe型の場合
かなりざっくりで、構文普通に間違ってると思いますが、
下記のような雰囲気でいかがでしょうか。

df_sample.iloc[:df_sample.count()*0.8,"coloum_name1"].sort_values(by=["coloum_name1"], ascending=True)

pythonのforは遅いです。cythonなどにすると早くなります。
http://www.utali.io/entry/2016/12/08/191713

投稿2017/08/29 17:45

miyamoto0105

総合スコア216

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

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

Hidd

2017/08/29 17:48

ありがとうございます。afterdf2はpandas型です。 やはりforは遅いのですね。。fortran等の古い言語に慣れているのでどうしてもpythonの機能が生かしきれていません。。 頂いたコードとリンクを試してみます。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問