web-dev-qa-db-ja.com

Python:pandasデータフレームでマルチプロセッシングを使用する

大きなデータセットでmultiprocessingを使用して、2つのgpsポイント間の距離を見つけたいです。テストセットを作成しましたが、multiprocessingをこのセットで動作させることができませんでした。

import pandas as pd
from geopy.distance import vincenty
from itertools import combinations
import multiprocessing as mp

df = pd.DataFrame({'ser_no': [1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
                'co_nm': ['aa', 'aa', 'aa', 'bb', 'bb', 'bb', 'bb', 'cc', 'cc', 'cc'],
                'lat': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
                'lon': [21, 22, 23, 24, 25, 26, 27, 28, 29, 30]})



def calc_dist(x):
    return pd.DataFrame(
               [ [grp,
                  df.loc[c[0]].ser_no,
                  df.loc[c[1]].ser_no,
                  vincenty(df.loc[c[0], x], 
                           df.loc[c[1], x])
                 ]
                 for grp,lst in df.groupby('co_nm').groups.items()
                 for c in combinations(lst, 2)
               ],
               columns=['co_nm','machineA','machineB','distance'])

if __name__ == '__main__':
    pool = mp.Pool(processes = (mp.cpu_count() - 1))
    pool.map(calc_dist, ['lat','lon'])
    pool.close()
    pool.join()

このエラーが発生した場合、Windows7 ProfessionalでAnaconda 2.5.0 64ビットでPython 2.7.11およびIpython 4.1.2を使用しています。

runfile( 'C:/.../ Desktop/multiprocessing test.py'、wdir = 'C:/.../ Desktop')トレースバック(最後の最後の呼び出し):

Runfile( 'C:/.../ Desktop/multiprocessing test.py'、wdir = 'C:/.../ Desktop')のファイル ""、1行目

ファイル「C:...\Local\Continuum\Anaconda2\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py」、699行目、runfile execfile(filename、namespace)

ファイル「C:...\Local\Continuum\Anaconda2\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py」、74行目、execfile exec(compile(scripttext、filename、 'exec')、glob 、loc)

Pool.map(calc_dist、['lat'、 'lon'])のファイル「C:/..../ multiprocessing test.py」の33行目

ファイル「C:...\AppData\Local\Continuum\Anaconda2\lib\multiprocessing\pool.py」、マップ内の行251、self.map_async(func、iterable、chunksize).get()

ファイル "C:...\Local\Continuum\Anaconda2\lib\multiprocessing\pool.py"、行567、get raise self._value

TypeError:1からPointインスタンスを作成できませんでした。

def get(self, timeout=None):
    self.wait(timeout)
    if not self._ready:
        raise TimeoutError
    if self._success:
        return self._value
    else:
        raise self._value
27
dustin

どうしましたか

コードの次の行:

pool.map(calc_dist, ['lat','lon'])

2つのプロセスを生成します-1つはcalc_dist('lat')を実行し、もう1つはcalc_dist('lon')を実行します。 doc の最初の例を比較してください。 (基本的に、pool.map(f, [1,2,3])は、f(1)f(2)、およびf(3)のリストで指定された引数を使用して、fを3回呼び出します。)間違っていない場合、関数calc_distcalc_dist('lat', 'lon')のみを呼び出すことができます。そして、それは並列処理を許可しません。

ソリューション

おそらく、各タプル(grp, lst)を個別のプロセスに送信して、プロセス間で作業を分割したいと考えています。次のコードはまさにそれを行います。

まず、分割の準備をしましょう。

grp_lst_args = list(df.groupby('co_nm').groups.items())

print(grp_lst_args)
[('aa', [0, 1, 2]), ('cc', [7, 8, 9]), ('bb', [3, 4, 5, 6])]

これらの各タプル(ここでは3つあります)を個別のプロセスの関数への引数として送信します。関数を書き直す必要があります。calc_dist2と呼びましょう。便宜上、引数はcalc_dist2(('aa',[0,1,2]))のようなタプルです

def calc_dist2(arg):
    grp, lst = arg
    return pd.DataFrame(
               [ [grp,
                  df.loc[c[0]].ser_no,
                  df.loc[c[1]].ser_no,
                  vincenty(df.loc[c[0], ['lat','lon']], 
                           df.loc[c[1], ['lat','lon']])
                 ]
                 for c in combinations(lst, 2)
               ],
               columns=['co_nm','machineA','machineB','distance'])

そして今、マルチプロセッシングがあります:

pool = mp.Pool(processes = (mp.cpu_count() - 1))
results = pool.map(calc_dist2, grp_lst_args)
pool.close()
pool.join()

results_df = pd.concat(results)

resultsは、(grp,lst)grp_lst_argsに対する呼び出しcalc_dist2((grp,lst))の結果(ここではデータフレーム)のリストです。 resultsの要素は、後で1つのデータフレームに連結されます。

print(results_df)
  co_nm  machineA  machineB          distance
0    aa         1         2  156.876149391 km
1    aa         1         3  313.705445447 km
2    aa         2         3  156.829329105 km
0    cc         8         9  156.060165391 km
1    cc         8         0  311.910998169 km
2    cc         9         0  155.851498134 km
0    bb         4         5  156.665641837 km
1    bb         4         6  313.214333025 km
2    bb         4         7  469.622535339 km
3    bb         5         6  156.548897414 km
4    bb         5         7  312.957597466 km
5    bb         6         7   156.40899677 km

ところで、Python 3では、with構造を使用できます。

with mp.Pool() as pool:
    results = pool.map(calc_dist2, grp_lst_args)

更新

このコードはLinuxでのみテストしました。 Linuxでは、読み取り専用データフレームdfは子プロセスからアクセスでき、メモリスペースにコピーされませんが、Windowsで正確に機能するかどうかはわかりません。 dfをチャンク(co_nmでグループ化)に分割し、これらのチャンクをcalc_distの他のバージョンへの引数として送信することを検討できます。

22
ptrj

奇妙な。 python2では動作するようですが、python3では動作しないようです。

これは、出力を印刷するための最小限の修正バージョンです。

import pandas as pd
from geopy.distance import vincenty
from itertools import combinations
import multiprocessing as mp

df = pd.DataFrame({'ser_no': [1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
                'co_nm': ['aa', 'aa', 'aa', 'bb', 'bb', 'bb', 'bb', 'cc', 'cc', 'cc'],
                'lat': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
                'lon': [21, 22, 23, 24, 25, 26, 27, 28, 29, 30]})



def calc_dist(x):
    ret =  pd.DataFrame(
               [ [grp,
                  df.loc[c[0]].ser_no,
                  df.loc[c[1]].ser_no,
                  vincenty(df.loc[c[0], x],
                           df.loc[c[1], x])
                 ]
                 for grp,lst in df.groupby('co_nm').groups.items()
                 for c in combinations(lst, 2)
               ],
               columns=['co_nm','machineA','machineB','distance'])
    print(ret)
    return ret

if __name__ == '__main__':
    pool = mp.Pool(processes = (mp.cpu_count() - 1))
    pool.map(calc_dist, ['lat','lon'])
    pool.close()
    pool.join()

そして、これはpython2からの出力です

0     aa         1         2  110.723608682 km
1     aa         1         3  221.460709525 km
2     aa         2         3  110.737100843 km
3     cc         8         9  110.827576495 km
4     cc         8         0  221.671650552 km
   co_nm  machineA  machineB          distance
5     cc         9         0  110.844074057 km
0     aa         1         2  110.575064814 km
1     aa         1         3  221.151481337 km
6     bb         4         5  110.765515243 km
2     aa         2         3  110.576416524 km
7     bb         4         6    221.5459187 km
3     cc         8         9  110.598565514 km
4     cc         8         0  221.203121352 km
8     bb         4         7  332.341640771 km
5     cc         9         0  110.604555838 km
6     bb         4         5   110.58113908 km
9     bb         5         6  110.780403457 km
7     bb         4         6  221.165643396 km
10    bb         5         7  221.576125528 km
8     bb         4         7  331.754177186 km
9     bb         5         6  110.584504316 km
10    bb         5         7  221.173038106 km
11    bb         6         7  110.795722071 km
11    bb         6         7   110.58853379 km

そして、これはpython3からのスタックトレース

"""
Traceback (most recent call last):
  File "/usr/local/lib/python3.4/dist-packages/geopy/point.py", line 123, in __new__
    seq = iter(arg)
TypeError: 'numpy.int64' object is not iterable

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.4/multiprocessing/pool.py", line 119, in worker
    result = (True, func(*args, **kwds))
  File "/usr/lib/python3.4/multiprocessing/pool.py", line 44, in mapstar
    return list(map(*args))
  File "gps.py", line 29, in calc_dist
    for grp, lst in df.groupby('co_nm').groups.items()
  File "gps.py", line 30, in <listcomp>
    for c in combinations(lst, 2)
  File "/usr/local/lib/python3.4/dist-packages/geopy/distance.py", line 322, in __init__
    super(vincenty, self).__init__(*args, **kwargs)
  File "/usr/local/lib/python3.4/dist-packages/geopy/distance.py", line 115, in __init__
    kilometers += self.measure(a, b)
  File "/usr/local/lib/python3.4/dist-packages/geopy/distance.py", line 342, in measure
    a, b = Point(a), Point(b)
  File "/usr/local/lib/python3.4/dist-packages/geopy/point.py", line 126, in __new__
    "Failed to create Point instance from %r." % (arg,)
TypeError: Failed to create Point instance from 8.
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "gps.py", line 38, in <module>
    pool.map(calc_dist, ['lat', 'lon'])
  File "/usr/lib/python3.4/multiprocessing/pool.py", line 260, in map
    return self._map_async(func, iterable, mapstar, chunksize).get()
  File "/usr/lib/python3.4/multiprocessing/pool.py", line 599, in get
    raise self._value
TypeError: Failed to create Point instance from 8.

私はこれが答えではないことを知っていますが、おそらく役立つでしょう...

2
salomonderossi