如何对两个熊猫数据框中的最近地理位置进行多处理查找?

时间:2019-08-14 14:29:03

标签: python python-3.x pandas multiprocessing geospatial

我有一个要并行应用的函数,在该函数中我调用了另一个函数,我认为该函数将从并行执行中受益。目标是在每个田地中吸收多年的农作物产量,并将所有作物合并为一个大熊猫数据框。我具有用于在每个数据帧中找到最接近点的函数,但是它非常费力并且需要一些时间。我希望加快速度。

我尝试创建一个池并在内部函数上使用map_async。我也尝试对外部函数的循环进行相同的操作。后者是我要做的唯一事情。我可以使用它,但是我知道必须有一种使其更快的方法。看看下面的代码:

return_columns = []
return_columns_cb = lambda x: return_columns.append(x)

def getnearestpoint(gdA, gdB, retcol):
    dist = lambda point1, point2: distance.great_circle(point1, point2).feet

    def find_closest(point):
        distances = gdB.apply(
            lambda row: dist(point, (row["Longitude"], row["Latitude"])), axis=1
        )
        return (gdB.loc[distances.idxmin(), retcol], distances.min())

    append_retcol = gdA.apply(
        lambda row: find_closest((row["Longitude"], row["Latitude"])), axis=1
    )
    return append_retcol

def combine_yield(field):
    #field is a list of the files for the field I'm working with
    #lots of pre-processing

    #dfs in this case is a list of the dataframes for the current field
    #mdf is the dataframe with the most points which I poppped from this list

    p = Pool()
    for i in range(0, len(dfs)):
        p.apply_async(getnearestpoint, args=(mdf, dfs[i], dfs[i].columns[-1]), callback=return_cols_cb)
    for col in return_columns:
        mdf = mdf.append(col)

    '''I unzip my points back to longitude and latitude here in the final 
       dataframe so I can write to csv without tuples'''

    mdf[["Longitude", "Latitude"]] = pd.DataFrame(
        mdf["Point"].tolist(), index=mdf.index
    )
    return mdf

def multiprocess_combine_yield():
    '''do stuff to get dictionary below with each field name as key and values 
     as all the files for that field'''
    yield_by_field = {'C01': ('files...'), ...}
    #The farm I'm working on has 30 fields and below is too slow
    for k,v in yield_by_field.items():
        combine_yield(v)

我想我需要帮助的是我想像使用一个池对字典中每个文件元组进行imap或apply_async的操作。然后,在将combined_yield函数应用于该文件元组时,我希望能够并行处理distance函数。该函数使程序陷入困境,因为它针对每年的产量计算每个数据框中每个点之间的距离。这些文件平均大约有1200个数据点,然后将所有这些乘以30个字段,我需要更好的东西。也许效率的提高在于找到更好的方法来拉近点。我仍然需要一些东西来提供gdB的值和距离,尽管这是因为稍后我会在从“ mdf”数据帧中选择要使用的行时做些什么。

1 个答案:

答案 0 :(得分:0)

感谢@ALollz评论,我明白了。我回到了getnearestpoint函数,而不是做一堆Series.apply,我现在使用的是cKDTree中的scipy.spatial来找到最接近的点,然后使用矢量化的Haversine计算每个匹配点的真实距离的距离。快得多。以下是以下代码的基础:

import numpy as np
import pandas as pd
from scipy.spatial import cKDTree

def getnearestpoint(gdA, gdB, retcol):
   gdA_coordinates = np.array(
   list(zip(gdA.loc[:, "Longitude"], gdA.loc[:, "Latitude"]))
)
    gdB_coordinates = np.array(
    list(zip(gdB.loc[:, "Longitude"], gdB.loc[:, "Latitude"]))
)
    tree = cKDTree(data=gdB_coordinates)
    distances, indices = tree.query(gdA_coordinates, k=1)

    #These column names are done as so due to formatting of my 'retcols'
    df = pd.DataFrame.from_dict(
    {
        f"Longitude_{retcol[:4]}": gdB.loc[indices, "Longitude"].values,
        f"Latitude_{retcol[:4]}": gdB.loc[indices, "Latitude"].values,
        retcol: gdB.loc[indices, retcol].values,
    }
)
    gdA = pd.merge(left=gdA, right=df, left_on=gdA.index, right_on=df.index)
    gdA.drop(columns="key_0", inplace=True)
    return gdA
def combine_yield(field):
    #same preprocessing as before

    for i in range(0, len(dfs)):
        mdf = getnearestpoint(mdf, dfs[i], dfs[i].columns[-1])

    main_coords = np.array(list(zip(mdf.Longitude, mdf.Latitude))) 
    lat_main = main_coords[:, 1]
    longitude_main = main_coords[:, 0]

    longitude_cols = [
        c for c in mdf.columns for m in [re.search(r"Longitude_B\d{4}", c)] if m
    ]
    latitude_cols = [
        c for c in mdf.columns for m in [re.search(r"Latitude_B\d{4}", c)] if m
    ]
    year_coords = list(zip_longest(longitude_cols, latitude_cols, fillvalue=np.nan))

    for i in year_coords:
        year = re.search(r"\d{4}", i[0]).group(0)
        year_coords = np.array(list(zip(mdf.loc[:, i[0]], mdf.loc[:, i[1]])))
        year_coords = np.deg2rad(year_coords)
        lat_year = year_coords[:, 1]
        longitude_year = year_coords[:, 0]
        diff_lat = lat_main - lat_year
        diff_lng = longitude_main - longitude_year
        d = (
            np.sin(diff_lat / 2) ** 2
            + np.cos(lat_main) * np.cos(lat_year) * np.sin(diff_lng / 2) ** 2
        )
        mdf[f"{year} Distance"] = 2 * (2.0902 * 10 ** 7) * np.arcsin(np.sqrt(d))
    return mdf

那我就做Pool.map(combine_yield, (v for k,v in yield_by_field.items())) 这产生了很大的变化。希望它可以帮助处于类似困境的任何人。