42854


python pandas 大量のデータフレームから特定の列を比較・条件に合った行を抽出して隣の列の値を更新したい

[code]

    [df1]
    id,  ip,      url,                            point
    4,   ,        https://www.google.co.jp,       0
    5,   8.8.8.8, ,                               0
    122, ,        https://ja.stackoverflow.com/,  0
    
    [df2]
    src, ip,  url,                            point
    92,  ,    https://stackoverflow.com/,     0
    111  ,    https://ja.stackoverflow.com/,  0
    115, ,    https://stackexchange.com,      0
    
    [df(x万件)]
    random_col , ip,     url,   point
    （ランダム）,（任意）,（任意）,0（初期値）
    
[/code]

というデータをもつデータフレームと、

[code]

    [point1_df]
    ip,       url,                  point
    8.8.8.8,  ,                     15
    ,         *.stackoverflow.com,  10
    ,         stackexchange.com,    8
    
    [point2_df]
    ip,       url,                  point
    ...（該当なし。）
    
    ...
    [point(x千件)_df]
    ip,         url,      point
    （固有値）, （固有値）, （一意）
    
[/code]

があったとして、

[code]

    [expected_df1]
    id,  ip,      url,                           point
    4,   ,        https://www.google.co.jp,      0
    5,   8.8.8.8, ,                              15
    122, ,        https://ja.stackoverflow.com/, 10
    
    [expected_df2]
    id,  ip,  url,                            point
    92,  ,    https://stackoverflow.com/,     10
    111  ,    https://ja.stackoverflow.com/,  10
    115, ,    https://stackexchange.com,      8
    
[/code]

のような結果を得るためにはどのようにすればよいでしょうか？

[注意]  
df(1,2, ... ,x)やpoint(1,2, ... ,x)_dfにはそれぞれのdf内で重複があります。  
dfの重複はそれぞれが別のものとして扱われます。  
point_dfの重複は無視できるものとします（先勝ち・後勝ちは不問）

・やりたくないこと

[code]

    select point1_df.point
    from df1,point1_df
    where df1.ip = point1_df.ip
    
    select point1_df.point
    from df2,point1_df
    where df1.url = point1_df.url
    
    select point1_df.point
    from df1,point1_df
    where df1.url = point1_df.url
    
[/code]

のようなSQLを書いて、ifなどで結果を判断して張り付けていくような書き方をするとコードがとてつもない事になるので、pandasのメソッドなどでスマートに実現できればと思っています。

[上手くいかなかった方法]

[code]

    point_dfs = [point1_df, point2_df, ... ,point(x)_df]
    merge_df = df1
    for point_df in point_dfs:
      merge_df = pd.merge(merge_df, point_df, on=ip)
      merge_df = pd.merge(merge_df, point_df, on=url)
    
[/code]

データ量が少なければこの方法でもいいんですが、データフレームを扱う時にforを使うと異様に遅くなる気がします。  
実際にforのような処理を求められる場合はリスト内包表記などで書いていますが、大きく改善は出来なかったので、あまりネストを深めないための措置という程度の認識で使うようにしています。

よろしくお願いします。


CC BY-SA 3.0
2018-03-26T07:12:46.847
42656
2018-07-01T01:31:14.360
27817
question
python pandas
python pandas 大量のデータフレームから特定の列を比較・条件に合った行を抽出して隣の列の値を更新したい


[上手くいかなかった方法]では、forで繰り返し処理をしています。Pythonのforは遅いので、最初に、point(x千件)_dfを一つのdfにまとめたら処理が早くなると思います。

[code]

    point_dfs = [point1_df, point2_df, ... ,point(x)_df]
    point_df = pd.concat(point_dfs)
    
    merge_df = df1
    merge_df = pd.merge(merge_df, point_df, on=ip)
    merge_df = pd.merge(merge_df, point_df, on=url)
    
[/code]


CC BY-SA 3.0
2018-04-02T01:15:24.733
42854
2018-04-02T01:15:24.733
15171
42656
answer


point(x千件)_dfは、`concat`で一つのdfにまとめてあるものとします。  
pandasは強力なツールで、やりたいこと事ができると思います。

ここでは、urlの方だけの処理を書いてみます。

[code]

    import pandas as pd
    import numpy as np
    from urllib.parse import urlparse
    
    #　point_dfの準備
    #　point_dfのサンプルデータ
    point_df = pd.DataFrame([['8.8.8.8', None, 15],
                         [None, '*.stackoverflow.com', 10],
                         [None, 'stackexchange.com',    8]],
                        columns = ['point_ip', 'point_url', 'point_point'])
    #　urlのデータがあるのもだけにする 
    point_df1 =  point_df.dropna(subset=['point_url']).copy()
    # point_df1 は、urlに*がないデータ、point_df2 は、urlに*があるデータで、*を削除しておく 
    point_df2 = point_df1[point_df1['point_url'].str[0]=='*'].copy()
    point_df1 = point_df1[point_df1['point_url'].str[0]!='*'].copy()
    point_df2['point_url'] = point_df2['point_url'].str[1:]
    
    # マージの処理
    # dfのサンプルデータ
    df1 = pd.DataFrame([[1, np.NaN, 'https://www.google.co.jp', 0],
                   [2, np.NaN, 'https://stackexchange.com', 0],
                   [3, np.NaN, 'https://ja.stackoverflow.com/', 0],
                   [4, np.NaN, 'https://aaa.cc/', 0],
                   [5, '8.8.8.8', np.NaN, 0]],
                   columns = ['id', 'ip', 'url', 'point'])
    # urlのデータがあるものだけにする
    df1_url = df1.dropna(subset=['url']).copy()
    # urlからhttps://等邪魔なものを消す
    df1_url['netloc'] = df1_url['url'].map(lambda x: urlparse(x).netloc)
    # urlに*がないpointデータとマージ
    df1_url = pd.merge(df1_url, point_df1, left_on = 'netloc', right_on = 'point_url', how = 'left')
    df1_url1 = df1_url.dropna(subset=['point_url'])
    # netlocのサブディレクトリを削除して、urlに*がないpointデータとマージできなかったデータを、urlに*があるpointデータとマージしてみる
    df1_url2 = df1_url[df1_url['point_url'].isnull()].loc[:,'id':'netloc']
    df1_url2['netloc'] = df1_url2['netloc'].map(lambda x: x[x.find('.'):])
    df1_url2 = pd.merge(df1_url2, point_df2, left_on = 'netloc', right_on = 'point_url', how = 'left')
    
[/code]

df1_url1 と df1_url2 を併せるとやりたい事になっていると思います。  
サブでディレクトリーの階層が深いものがあるのであれば、`'netloc'`の頭のサブディレクトリーを削除する処理を繰り返してください。


CC BY-SA 3.0
2018-04-06T01:57:03.897
42979
2018-04-06T01:57:03.897
15171
42656
answer