问题
I have two dataframes. Each one contains locations (X,Y) and a value for that point. For each point in the first dataframe I want to find the closest point in the second dataframe and then find the difference. I have code that is working, but it uses a for loop, which is slow.
Any suggestions for how to speed this up? I know that it is generally a good idea to get rid of for loops in pandas, for performance, but I don't see how to do that in this case.
Here is some sample code:
import pandas as pd
import numpy as np
df1=pd.DataFrame(np.random.rand(10,3), columns=['val', 'X', 'Y'])
df2=pd.DataFrame(np.random.rand(10,3), columns=['val', 'X', 'Y'])
nearest=df1.copy() #CORRECTION. This had been just =df1 which caused a problem when trying to compare to answers submitted.
for idx,row in nearest.iterrows():
#Find the X,Y points closest to the selected point:
closest=df2.ix[((df2['X']-row['X'])**2+(df2['Y']-row['Y'])**2).idxmin()]
#Set the max to the difference between the current row and the nearest one.
nearest.loc[idx,'val']= df1.loc[idx,'val'] - closest['val']
As I am using this on larger dataframes, it takes a long time to do the calculation.
Thanks,
回答1:
One cool solution to your problem involves leveraging the complex
data type (builtin in python and numpy).
import numpy as np
import pandas as pd
df1=pd.DataFrame(np.random.rand(10,3), columns=['val', 'X', 'Y'])
df2=pd.DataFrame(np.random.rand(10,3), columns=['val', 'X', 'Y'])
# dataframes to numpy arrays of complex numbers
p1 = (df1['X'] + 1j * df1['Y']).values
p2 = (df2['X'] + 1j * df2['Y']).values
# calculate all the distances, between each point in
# df1 and each point in df2 (using an array-broadcasting trick)
all_dists = abs(p1[..., np.newaxis] - p2)
# find indices of the minimal distance from df1 to df2,
# and from df2 to df1
nearest_idxs1 = np.argmin(all_dists, axis = 0)
nearest_idxs2 = np.argmin(all_dists, axis = 1)
# extract the rows from the dataframes
nearest_points1 = df1.ix[nearest_idxs1].reset_index()
nearest_points2 = df2.ix[nearest_idxs2].reset_index()
This is probably much faster than using a loop, but if your series turn out to be huge, it will consume a lot of memory (quadratic in number of points).
Also, this solution works if the sets of points are of different lenths.
Here's a concrete example demostrating how this works:
df1 = pd.DataFrame([ [987, 0, 0], [888, 2,2], [2345, 3,3] ], columns=['val', 'X', 'Y'])
df2 = pd.DataFrame([ [ 1000, 1, 1 ], [2000, 9, 9] ] , columns=['val', 'X', 'Y'])
df1
val X Y
0 987 0 0
1 888 2 2
2 2345 3 3
df2
val X Y
0 1000 1 1
1 2000 9 9
Here, for every point in df1, df2[0]=(1,1) is the nearest point (as shown in nearest_idxs2
below). Considering the opposite problem, for (1,1), either (0,0) or (2,2) are the nearest, and for (9,9), df1[1]=(3,3) is the nearest (as shown in nearest_idxs1
below).
p1 = (df1['X'] + 1j * df1['Y']).values
p2 = (df2['X'] + 1j * df2['Y']).values
all_dists = abs(p1[..., np.newaxis] - p2)
nearest_idxs1 = np.argmin(all_dists, axis = 0)
nearest_idxs2 = np.argmin(all_dists, axis = 1)
nearest_idxs1
array([0, 2])
nearest_idxs2
array([0, 0, 0])
# It's nearest_points2 you're after:
nearest_points2 = df2.ix[nearest_idxs2].reset_index()
nearest_points2
index val X Y
0 0 1000 1 1
1 0 1000 1 1
2 0 1000 1 1
df1['val'] - nearest_points2['val']
0 -13
1 -112
2 1345
To solve the opposite problem (for each point in df2, find nearest in df1), take nearest_points1
and df2['val'] - nearest_points1['val']
来源:https://stackoverflow.com/questions/28612773/how-to-speed-up-nearest-search-in-pandas-perhaps-by-vectorizing-code