我有一个37456153行x 3列Pandas数据帧,包含以下列:[Timestamp, Span, Elevation]
。每个Timestamp
值都有大约62000行Span
和Elevation
数据,看起来像(例如,在Timestamp = 17210
过滤时):
Timestamp Span Elevation
94614 17210 -0.019766 36.571
94615 17210 -0.019656 36.453
94616 17210 -0.019447 36.506
94617 17210 -0.018810 36.507
94618 17210 -0.017883 36.502
... ... ... ...
157188 17210 91.004000 33.493
157189 17210 91.005000 33.501
157190 17210 91.010000 33.497
157191 17210 91.012000 33.500
157192 17210 91.013000 33.503
如上所示,Span
数据的间隔不等,我实际需要它。所以我想出了以下代码将其转换为等间距格式。我知道我想分析的start
和end
位置。然后我将delta
参数定义为我的增量。我创建了一个名为mesh
的numpy数组,它保存了我想要最终的等间距Span
数据。最后,我决定对给定TimeStamp
(代码中的17300)的数据帧进行迭代,以测试它的工作速度。代码中的for循环计算每个增量的+/- Elevation
范围的平均0.5delta
值。
我的问题是:过滤数据帧需要603毫秒,并在单次次迭代时计算平均值Elevation
。对于给定的参数,我必须经历9101次迭代,从而导致该循环结束的大约1.5小时的计算时间。此外,这是一个Timestamp
值,我有600个(900小时做所有?!)。
有什么方法可以加快这个循环吗?非常感谢任何输入!
# MESH GENERATION
start = 0
end = 91
delta = 0.01
mesh = np.linspace(start,end, num=(end/delta + 1))
elevation_list =[]
#Loop below will take forever to run, any idea about how to optimize it?!
for current_loc in mesh:
average_elevation = np.average(df[(df.Timestamp == 17300) &
(df.Span > current_loc - delta/2) &
(df.Span < current_loc + delta/2)].Span)
elevation_list.append(average_elevation)
答案 0 :(得分:6)
您可以使用np.searchsorted
对整个事物进行矢量化。我不是一个大熊猫用户,但这样的东西应该工作,并在我的系统上运行得相当快。使用chrisb的虚拟数据:
In [8]: %%timeit
...: mesh = np.linspace(start, end, num=(end/delta + 1))
...: midpoints = (mesh[:-1] + mesh[1:]) / 2
...: idx = np.searchsorted(midpoints, df.Span)
...: averages = np.bincount(idx, weights=df.Elevation, minlength=len(mesh))
...: averages /= np.bincount(idx, minlength=len(mesh))
...:
100 loops, best of 3: 5.62 ms per loop
这比你的代码快3500倍:
In [12]: %%timeit
...: mesh = np.linspace(start, end, num=(end/delta + 1))
...: elevation_list =[]
...: for current_loc in mesh:
...: average_elevation = np.average(df[(df.Span > current_loc - delta/2) &
...: (df.Span < current_loc + delta/2)].Span)
...: elevation_list.append(average_elevation)
...:
1 loops, best of 3: 19.1 s per loop
编辑那么这是如何运作的?在midpoints
中,我们存储了桶之间边界的排序列表。然后,我们在此排序列表上使用searchsorted
进行二进制搜索,并获取idx
,它基本上告诉我们每个数据点属于哪个存储桶。剩下的就是对每个桶中的所有值进行分组。这是bincount
的用途。给定一系列整数,它计算每个数字出现的次数。给定一组int,以及相应的weights
数组,而不是为存储桶的计数器添加1,它会在weights
中添加相应的值。通过两次调用bincount
,您可以得到每个桶的总和和项目数:除以它们,您就得到了桶的平均值。
答案 1 :(得分:1)
这是一个想法 - 可能仍然太慢,但我想我会分享。首先,一些虚拟数据。
df = pd.DataFrame(data={'Timestamp': 17210,
'Span': np.linspace(-1, 92, num=60000),
'Elevation': np.linspace(33., 37., num=60000)})
然后,获取您创建的网格数组,将其转换为数据框,并添加移位条目,以便数据框中的每个条目代表新偶数跨度的一步。
mesh_df = pd.DataFrame(mesh, columns=['Equal_Span'])
mesh_df['Equal_Span_Prev'] = mesh_df['Equal_Span'].shift(1)
mesh_df = mesh_df.dropna()
接下来,我想根据两个Equal_Span
列之间的条目将此数据框与更大的数据集连接起来。在pandas中可能有一种方法,但笛卡尔式连接似乎更容易在SQL中表达,所以首先,我将所有数据传送到内存中的sqlite数据库。如果遇到内存问题,请将其作为基于文件的数据库。
import sqlite3
con = sqlite3.connect(':memory:')
df.to_sql('df', con, index=False)
mesh_df.to_sql('mesh_df', con, index=False)
这是主查询。在我的测试数据上花了大约1分30秒,所以这可能仍需要很长时间才能完成整个数据集。
join_df = pd.read_sql("""SELECT a.Timestamp, a.Span, a.Elevation, b.Equal_Span
FROM df a, mesh_df b
WHERE a.Span BETWEEN b.Equal_Span_Prev AND b.Equal_Span""", con)
但是一旦数据处于这种形式,就可以轻松/快速地获得所需的平均值。
join_df.groupby(['Timestamp','Equal_Span'])['Elevation'].mean()