gpt4 book ai didi

python - 如何加速遍历数组/矩阵?尝试了 pandas 和 numpy 数组

转载 作者:行者123 更新时间:2023-12-05 07:36:33 25 4
gpt4 key购买 nike

我想通过大型二维数组 (15,100m) 进行一些特征丰富。

处理包含 100,000 条记录的示例集表明我需要更快地完成此操作。

编辑(数据模型信息)

为简化起见,假设我们只有两个相关列:

  • IP(标识符)
  • Unix(自 1970 年以来以秒为单位的时间戳)

我想添加第 3 列,计算该 IP 在过去 12 小时内出现的次数。

结束编辑

我的第一次尝试是使用 pandas,因为它很适合使用命名维度,但速度太慢:

for index,row in tqdm_notebook(myData.iterrows(),desc='iterrows'):
# how many times was the IP address (and specific device) around in the prior 5h?
hours = 12
seen = myData[(myData['ip']==row['ip'])
&(myData['device']==row['device'])
&(myData['os']==row['os'])
&(myData['unix']<row['unix'])
&(myData['unix']>(row['unix']-(60*60*hours)))].shape[0]
ip_seen = myData[(myData['ip']==row['ip'])
&(myData['unix']<row['unix'])
&(myData['unix']>(row['unix']-(60*60*hours)))].shape[0]
myData.loc[index,'seen'] = seen
myData.loc[index,'ip_seen'] = ip_seen

然后我切换到 numpy 数组并希望得到更好的结果,但它仍然太慢无法针对完整数据集运行:

# speed test numpy arrays
for i in np.arange(myArray.shape[0]):
hours = 12
ip,device,os,ts = myArray[i,[0,3,4,12]]
ip_seen = myArray[(np.where((myArray[:,0]==ip)
& (myArray[:,12]<ts)
& (myArray[:,12]>(ts-60*60*hours) )))].shape[0]
device_seen = myArray[(np.where((myArray[:,0]==ip)
& (myArray[:,2] == device)
& (myArray[:,3] == os)
& (myArray[:,12]<ts)
& (myArray[:,12]>(ts-60*60*hours) )))].shape[0]
myArray[i,13]=ip_seen
myArray[i,14]=device_seen

我的下一个想法是只迭代一次,并维护一个不断增长的当前计数字典,而不是在每次迭代时都向后看。

但这会有一些其他缺点(例如,如何跟踪何时减少超出 12 小时窗口的观测值的计数)。

您将如何解决这个问题?

甚至可以选择使用低级 Tensorflow 函数来涉及 GPU 吗?

谢谢

最佳答案

加快速度的唯一方法是循环。在您的情况下,您可以尝试使用 rolling使用您想要的时间跨度窗口,使用 Unix 时间戳作为日期时间索引(假设记录按时间戳排序,否则您需要先排序)。这对于 ip_seen 应该可以正常工作:

ip = myData['ip']
ip.index = pd.to_datetime(myData['unix'], unit='s')
myData['ip_seen'] = ip.rolling('5h')
.agg(lambda w: np.count_nonzero(w[:-1] == w[-1]))
.values.astype(np.int32)

但是,当聚合涉及多个列时,例如在 seen 列中,它会变得更加复杂。目前(参见 Pandas issue #15095)滚动函数不支持跨越两个维度的聚合。一种解决方法是将感兴趣的列合并到一个新的系列中,例如元组(如果值是数字可能会更好)或字符串(如果值已经是字符串可能会更好)。例如:

criteria = myData['ip'] + '|' + myData['device'] + '|' + myData['os']
criteria.index = pd.to_datetime(myData['unix'], unit='s')
myData['seen'] = criteria.rolling('5h')
.agg(lambda w: np.count_nonzero(w[:-1] == w[-1]))
.values.astype(np.int32)

编辑

显然 rolling仅适用于数字类型,剩下两个选项:

  1. 处理数据以使用数字类型。对于 IP,这很容易,因为它实际上代表一个 32 位数字(如果我猜是 IPv6,则为 64)。对于设备和操作系统,假设它们现在是字符串,它会变得更加复杂,您必须将每个可能的值映射到一个整数并将其与长值中的 IP 合并,例如将它们放在更高位或类似的位置(对于 IPv6 甚至可能是不可能的,因为 NumPy 目前支持的最大整数是 64 位)。
  2. 翻转 myData 的索引(现在应该是 not 日期时间,因为 rolling 也不能使用它)和使用索引窗口获取必要的数据并进行操作:

    # Use sequential integer index
    idx_orig = myData.index
    myData.reset_index(drop=True, inplace=True)
    # Index to roll
    idx = pd.Series(myData.index)
    idx.index = pd.to_datetime(myData['unix'], unit='s')
    # Roll aggregation function
    def agg_seen(w, data, fields):
    # Use slice for faster data frame slicing
    slc = slice(int(w[0]), int(w[-2])) if len(w) > 1 else []
    match = data.loc[slc, fields] == data.loc[int(w[-1]), fields]
    return np.count_nonzero(np.all(match, axis=1))
    # Do rolling
    myData['ip_seen'] = idx.rolling('5h') \
    .agg(lambda w: agg_seen(w, myData, ['ip'])) \
    .values.astype(np.int32)
    myData['ip'] = idx.rolling('5h') \
    .agg(lambda w: agg_seen(w, myData, ['ip', 'device', 'os'])) \
    .values.astype(np.int32)
    # Put index back
    myData.index = idx_orig

    不过,这不是 rolling 的用途,我不确定这是否比循环提供更好的性能。

关于python - 如何加速遍历数组/矩阵?尝试了 pandas 和 numpy 数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49172221/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com