@pythontaro

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Pandas処理時間をもっと縮める方法?

解決したいこと

Pandasで複数行のデータから判断して特定カラムにデータをセットしています。
処理時間をもっと縮める方法はないでしょうか?

発生している問題・エラー

C:\anaconda3\lib\site-packages\pandas\core\indexing.py:1765: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  isetter(loc, value)

該当するソースコード

%%time
wdf = dft1 # 関数に変更時に使うためワーク変数を用意
wptn = 0
if wdf.iloc[0,3]=='B':  #1
    wptn = wptn + 512
if wdf.iloc[1,3]=='B':
    wptn = wptn + 256
if wdf.iloc[2,3]=='B':
    wptn = wptn + 128
if wdf.iloc[3,3]=='B':
    wptn = wptn + 64
if wdf.iloc[4,3]=='B':
    wptn = wptn + 32
if wdf.iloc[5,3]=='B':
    wptn = wptn + 16
if wdf.iloc[6,3]=='B':
    wptn = wptn + 8
if wdf.iloc[7,3]=='B':
    wptn = wptn + 4
if wdf.iloc[8,3]=='B':  #9
    wptn = wptn + 2

for i in range(9,len(wdf)-1):
    if wdf.iloc[i,3]=='B':
        wptn = wptn + 1
    wdf.iloc[i+1, 4] = wptn
    wptn = (wptn * 2) & 0b1111111111

自分で試したこと

下図のようにFor分を使い カラム(kei1_color)の直前10行分データを、R=1,B=0と置き換えて 10進数に変換したものを カラム(prv_ptn_no)に順次セットしていく。
0506 0626261.png

0 likes

2Answer

Numpyの畳み込み演算を使うのが速いでしょう。
"kei1_color"列のRとBを変換します。
②配列を畳み込みます。
③データフレームに挿入します。

import numpy as np

n = 10  # 畳み込む数

arr = (data["kei1_color"] == 'B').to_numpy()
values = np.convolve(arr, 2**np.arange(n))[:-(n-1)]
data["prv_ptn_no"] = values
2Like

Comments

  1. @pythontaro

    Questioner

    ありがとうございます。もともと仕事が組込み系でアセンブラ、C、C++を使っているのですが、仕様が全く違い困惑します。私みたいなものが、学習工数を最小限に仕様を理解する参考情報があれば教えていただけませんでしょうか?
  2. 基本的にはやりたいことがあるたびに毎度方法を検索していますが、自分はデータの収集・集計を行うことが多くpandasとNumPyは頻繁に使うので、それらの公式のドキュメントを読んでこんな関数があるんだなというの勉強していました(どういう関数があるか知っておけば応用がきく)。

こんにちは。
Pandasはちょっと触ったことがある程度なのですが、for文で回すとすげー遅いということは知っています。
複数の行にまたがった操作をしようとしていることが面倒の元なので、

# R->1, B->0としたSeriesを先に作る
color = data["kei1_color"].map({"R": 0, "B": 1})

とした上で、このSeriesを1行ずつずらして元のDataFrameにマージしてしまえば1行の中に計算の元となる値が入ります。(この辺はRDB的な考え方です。indexが連続でない場合は頑張ってください)

for i in range(0, 10):
    color.name = f"color_{i}"
    data = pd.merge(data, color, left_index=True, right_index=True, how="left")
    color.index = color.index + 1

data["prv_ptn_no"] = data.color_9 * 512 + \
                     data.color_8 * 256 + \
                     data.color_7 * 128 + \
                     data.color_6 * 64 + \
                     data.color_5 * 32 + \
                     data.color_4 * 16 + \
                     data.color_3 * 8 + \
                     data.color_2 * 4 + \
                     data.color_1 * 2 + \
                     data.color_0

60万行での計測はしていませんが、手元の環境で6万行が一瞬だったのでまあ何秒もかからないでしょう。

>>> data.loc[9:15, ["tno", "id", "time", "kei1_color", "prv_ptn_no"]]
    tno    id                 time kei1_color  prv_ptn_no
9     1  11.0  2021-05-06 00:00:00          B       443.0
10    1  12.0  2021-05-06 00:00:00          R       886.0
11    1  13.0  2021-05-06 00:00:00          B       749.0
12    1  14.0  2021-05-06 00:00:00          R       474.0
13    1  15.0  2021-05-06 00:00:00          R       948.0
14    1  16.0  2021-05-06 00:00:00          B       873.0
15    1  17.0  2021-05-06 00:00:00          R       722.0
1Like

Comments

  1. @pythontaro

    Questioner

    ご回答ありがとうございます。
    f"color_{i}"の説明をお願いできませんか?
    Python初学者なものでよろしくおねがいします。
  2. @nkayさんが圧倒的スマートな解法を示している前でだいぶ恥ずかしいものがありますが……
    for を展開するとこうなります。

    color1 = color.copy()
    color1.name = "color_1" # これ
    color1.index = color.index + 1
    data = pd.merge(data, color1, left_index=True, right_index=True, how="left")

    color2 = color.copy()
    color2.name = "color_2"
    color2.index = color.index + 2
    data = pd.merge(data, color2, left_index=True, right_index=True, how="left")

    1行ずつスライドしながら `data` に列が追加されていくのが分かると思います。
    列名が重複しないように毎度名前を変えています。
  3. @pythontaro

    Questioner

    ありがとうございました。よく理解できました。

Your answer might help someone💌