Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/backbone.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 熊猫快速应用方法_Python_Pandas - Fatal编程技术网

Python 熊猫快速应用方法

Python 熊猫快速应用方法,python,pandas,Python,Pandas,我有一个函数,我正试图应用到位置的数据帧。具体来说,我想添加一个新列,其中包含每个站点最近的10个站点。下面的方法似乎有效,但速度极其缓慢 def distance(first_lat, first_lon, second_lat, second_lon): return ((first_lat - second_lat) ** 2 + (first_lon - second_lon) ** 2) ** 0.5 def load_site_list(): ''' Th

我有一个函数,我正试图应用到位置的数据帧。具体来说,我想添加一个新列,其中包含每个站点最近的10个站点。下面的方法似乎有效,但速度极其缓慢

def distance(first_lat, first_lon, second_lat, second_lon):
    return ((first_lat - second_lat) ** 2 + (first_lon - second_lon) ** 2) ** 0.5


def load_site_list():
    '''
    This function generates a dataframe with all the available sites
    '''
    url = 'ftp://ftp.ncdc.noaa.gov/pub/data/noaa/isd-history.csv'
    cols = ["STATION NAME",
            "LAT",
            "LON"]
    df = pd.read_csv(url, parse_dates=False, usecols=cols)
    df = df.dropna(subset=['LAT'])
    df = df.dropna(subset=['LON'])
    df['LAT'] = df['LAT'].astype(float)
    df['LON'] = df['LON'].astype(float)
    return df

sites = load_site_list()
sites['closest'] = ""
for index, row in sites.iterrows():
    sites['dist'] = sites.apply(lambda line: distance(line['LAT'], line['LON'], row['LAT'], row['LON']), axis=1)
    sites.sort_values('dist', inplace=True)
    sites['closest'][index] = sites['STATION NAME'].iloc[1:11].tolist()

在for循环中,生成与当前列的距离的第一行每循环占用一秒钟。这个循环中有10000多行……有没有更快的方法呢?

请注意,您的代码的时间复杂度为O(n^2):在这种情况下,您在for循环中的apply函数(即纯Python)中计算30k*30k=9亿个距离

pandas中的向量操作是用C实现的,因此如果在单个向量操作中计算所有距离,则会得到相对加速

如果你有足够的内存,你可以进行笛卡尔连接,计算所有的成对距离,然后进行排序,分组,然后取头,像这样:

# code to reduce memory usage
sites['site_code'] = pd.Categorical(sites['STATION NAME']).codes
sites['LAT'] = sites.LAT.astype(np.float16)
sites['LON'] = sites.LAT.astype(np.float16)
sites_small = sites[['site_code','LAT','LON']].copy()
sites_small.index = [0]*len(sites_small)

pairs = sites_small.join(sites_small,lsuffix='_x',rsuffix='_y')
pairs['dist'] = (pairs['LAT_x'] - pairs['LAT_y'])**2 + (pairs['LON_x'] - pairs['LON_y'])**2
pairs.sort_values(['STATION NAME_x','dist'], inplace = True) # actually, just sorting by dist is sufficient
pairs.groupby('STATION NAME_x').head(10)
不幸的是,您可能没有足够的RAM:如果您将站点名称编码为16位整数,将坐标编码为16位浮点,则每行需要12个字节(因为您正在查看对),再加上索引的8个字节(pandas将这些字节放入联接中的longints;我不知道如何解决这个问题),对于最终的数据帧,计算结果约为20字节*900m行=18GB。在实践中可能更多,并且操作期间的峰值内存使用率高于此值(特别是排序将花费最长的时间,并使用大量内存)

我在我的机器上尝试了这一点:我使用了大约30GB,放弃了等待完整排序,而是对
dist
小于100的子集进行排序。不到5分钟,大部分时间都花在加入上

在一天结束时,你会看到近10亿次计算;如果您希望以C的速度执行此操作,而不必存储所有成对数据(pandas中的direct方法就是这种情况),那么您很可能需要使用numpy数组和/或多处理在Cython中编写代码

更聪明的方法是避免进行10亿次计算,这涉及到知道哪些距离不需要计算。这需要一些巧妙的逻辑,但幸运的是,这是一个经过充分研究的k-最近邻主题,它有专门为此类问题设计的高效算法:

from sklearn.neighbors import NearestNeighbors
data = sites[['LAT','LON']].values
nbrs = NearestNeighbors(n_neighbors=10, algorithm='auto', metric = 'euclidean').fit(data)
distances, indices = nbrs.kneighbors(data)
indices
这需要不到一秒钟的时间来计算。恢复最近邻居的名称需要更长的时间:

df = pd.DataFrame(indices, index = sites['STATION NAME'].values)
df.replace(dict(enumerate(sites['STATION NAME'].values)), inplace = True)

(通过使用带有一些堆叠/取消堆叠的
.merge()
方法,您实际上可以大大加快速度,但在这种情况下,由于数据包含重复项,因此会稍微复杂一些。)

您是否考虑过使用一种方法?尝试将您的
站点
数据帧作为一个numpy数组(或多个数组)然后在最后创建数据帧。您可以使用更快的,似乎工作得更好。10分钟而不是3小时。我将做一些数据清理。如果删除重复项,您将如何使用合并方法?
df.stack()
为您提供一列站点索引;将其转换为数据帧,并将其与
pd.dataframe(枚举(站点['STATION NAME'].values))
合并。