簡體   English   中英

一種快速方法,可從一個數據框的另一個數據框中查找元素並返回其索引

[英]Fast approach to find elements from one Dataframe in another and return their indexes

簡而言之,我試圖將第一個DataFrame 2列中的DataFrame與另一個DataFrame的相同列進行DataFrame 匹配的行的索引作為新列存儲在第一個DataFrame

讓我解釋一下 :我正在使用地理特征(緯度/經度),並且主要的DataFrame (稱為df )具有約55M的觀測值,看起來有點像這樣:

在此處輸入圖片說明

如您所見,只有兩行數據看起來合法(索引2和4)。

第二個DataFrame (稱為legit_df要小得多,並且擁有我認為合法的所有地理數據:

在此處輸入圖片說明

無需研究WHY,主要任務包括將df每個緯度/經度觀測值與legit_df的數據進行legit_df 匹配成功后,將legit_df的索引復制到df的新列中,導致df如下所示:

在此處輸入圖片說明

-1用於顯示沒有成功匹配的時間。 在上面的示例中,唯一有效的觀察是索引2和4的觀察,它們在legit_df索引1和2處找到了匹配legit_df

我當前解決此問題的方法使用.apply() 是的,它很慢,但是我找不到一種方法來對下面的函數進行矢量化或使用Cython對其進行加速:

def getLegitLocationIndex(lat, long):
    idx = legit_df.index[(legit_df['pickup_latitude'] == lat) & (legit_df['pickup_longitude'] == long)].tolist()
    if (not idx):
        return -1
    return idx[0]

df['legit']  = df.apply(lambda row: getLegitLocationIndex(row['pickup_latitude'], row['pickup_longitude']), axis=1)

由於此代碼在觀察到55M的DataFrame上非常慢,因此我的問題是 :是否有更快的方法來解決此問題?

我正在分享一個簡短,自包含,正確(可編譯)的示例,幫助您提出更快速的選擇:

import pandas as pd
import numpy as np

data1 = { 'pickup_latitude'  : [41.366138,   40.190564,  40.769413],
          'pickup_longitude' : [-73.137393, -74.689831, -73.863300]
        }

legit_df = pd.DataFrame(data1)
display(legit_df)

####################################################################################

observations = 10000
lat_numbers = [41.366138,   40.190564,  40.769413, 10, 20, 30, 50, 60, 80, 90, 100]
lon_numbers = [-73.137393, -74.689831, -73.863300, 11, 21, 31, 51, 61, 81, 91, 101]

# Generate 10000 random integers between 0 and 10
random_idx = np.random.randint(low=0, high=len(lat_numbers)-1, size=observations)
lat_data = []
lon_data = []

# Create a Dataframe to store 10000 pairs of geographical coordinates
for i in range(observations):
    lat_data.append(lat_numbers[random_idx[i]])
    lon_data.append(lon_numbers[random_idx[i]])

df = pd.DataFrame({ 'pickup_latitude' : lat_data, 'pickup_longitude': lon_data })
display(df.head())

####################################################################################

def getLegitLocationIndex(lat, long):
    idx = legit_df.index[(legit_df['pickup_latitude'] == lat) & (legit_df['pickup_longitude'] == long)].tolist()
    if (not idx):
        return -1
    return idx[0]


df['legit']  = df.apply(lambda row: getLegitLocationIndex(row['pickup_latitude'], row['pickup_longitude']), axis=1)
display(df.head())

上面的示例創建的df僅具有10k observations ,這在我的計算機上運行大約需要7秒鍾。 進行10萬次observations ,大約需要67秒才能運行。 現在想象一下當我必須處理5500萬行時的痛苦...

我認為您可以使用合並而不是當前邏輯來顯着加快此速度:

full_df = df.merge(legit_df.reset_index(), how="left", on=["pickup_longitude", "pickup_latitude"])

這將重置參考表的索引以使其成為列並按經度連接

full_df = full_df.rename(index = str, columns={"index":"legit"})
full_df["legit"] = full_df["legit"].fillna(-1).astype(int)

這將重命名為您想要的列名稱,並使用-1填充連接列中的所有缺失項

基准:

舊方法: 5.18 s ± 171 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

新方法: 23.2 ms ± 1.3 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

您可以將DataFrame.merge與公用鍵上的how='left'一起使用。 首先重置legit_df的索引。

然后用-1 fillna

df.merge(legit_df.reset_index(), on=['pickup_latitude', 'pickup_longitude'], how='left').fillna(-1)

測試性能:

%%timeit
df['legit']  = df.apply(lambda row: getLegitLocationIndex(row['pickup_latitude'], row['pickup_longitude']), axis=1)

每個循環5.81 s±179毫秒(平均±標准偏差,共7次運行,每個循環1次)

%%timeit
(df.merge(legit_df.reset_index(),on=['pickup_latitude', 'pickup_longitude'], how='left').fillna(-1))

每個循環6.27 ms±254 µs(平均±標准偏差,共運行7次,每個循環100個)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM