【问题标题】:Using `split` on columns too slow - how can I get better performance?在列上使用 `split` 太慢 - 我怎样才能获得更好的性能?
【发布时间】:2015-07-18 18:14:13
【问题描述】:

我有一个通话记录数据集(大约 10Gb)。有一列包含 IP 地址,我想将其拆分为四个新列。我正在尝试使用:

df['ip'].fillna('0.0.0.0', inplace=True)
df = df.join(df['ip'].apply(lambda x: Series(x.split('.'))))

但它太慢了...fillna 很快,就像 10 秒,但它会在拆分中停留 5 小时...

有没有更好的办法?

【问题讨论】:

  • 你是如何加载数据的?

标签: python performance pandas split


【解决方案1】:

示例数据(如果您提供此数据,您的问题更有可能得到解答):

import pandas as pd
import random

def make_ip():
    return '.'.join(str(random.randint(0, 255)) for n in range(4))

df = pd.DataFrame({'ip': [make_ip() for i in range(20)]})

df
Out[4]: 
                 ip
0     153.1.219.147
1   110.170.184.123
2     91.100.92.150
3      61.148.99.64
4      94.175.253.3
5     30.29.220.218
6     7.118.167.173
7       71.99.78.94
8   240.122.200.194
9       48.16.177.0
10    81.155.96.173
11     202.91.134.9
12   90.155.159.176
13     169.74.28.73
14   149.133.115.45
15   168.196.41.132
16   145.195.15.234
17     12.200.28.27
18    146.255.29.80
19   228.226.143.45

使用 pandas 的内置 str 方法进行高效的字符串操作,并直接添加它们以避免连接缓慢:

df[['ip0', 'ip1', 'ip2', 'ip3']] = df.ip.str.split('.', return_type='frame')

df
Out[8]: 
                 ip  ip0  ip1  ip2  ip3
0     153.1.219.147  153    1  219  147
1   110.170.184.123  110  170  184  123
2     91.100.92.150   91  100   92  150
3      61.148.99.64   61  148   99   64
4      94.175.253.3   94  175  253    3
5     30.29.220.218   30   29  220  218
6     7.118.167.173    7  118  167  173
7       71.99.78.94   71   99   78   94
8   240.122.200.194  240  122  200  194
9       48.16.177.0   48   16  177    0
10    81.155.96.173   81  155   96  173
11     202.91.134.9  202   91  134    9
12   90.155.159.176   90  155  159  176
13     169.74.28.73  169   74   28   73
14   149.133.115.45  149  133  115   45
15   168.196.41.132  168  196   41  132
16   145.195.15.234  145  195   15  234
17     12.200.28.27   12  200   28   27
18    146.255.29.80  146  255   29   80
19   228.226.143.45  228  226  143   45

【讨论】:

  • 可能会显示应用与使用 str ops 的时间 :)
  • 这实际上似乎并没有更快。
  • --- processIP 5.261000156403 seconds ---(我发布的方法),和你的 --- processIP 5.238000154495 seconds ---,在一个简短的数据集中......
  • @cge,哇,我的印象是.str方法应该是优化的,所以看了这个问题,可以立即看到规范的pandas API方法来做到这一点,并张贴。不过看起来修复工作正在进行中。
  • 是的,看起来这是 str.split 中的一个简单错误,导致性能下降。在不久的将来,您的答案应该是最好的!
【解决方案2】:

原来Pandas中的str.split(在core/strings.py中作为str_split)其实很慢;它不再高效,并且仍然使用 Python 进行迭代,没有提供任何加速。

其实,见下文。 Pandas 在这方面的表现简直惨不忍睹;这不仅仅是 Python 与 C 的迭代,因为使用 Python 列表做同样的事情是最快的方法!

不过,有趣的是,有一个更快的技巧解决方案:将 Series 写入文本,然后使用 '.' 再次读取它。作为分隔符:

df[['ip0', 'ip1', 'ip2', 'ip3']] = \
    pd.read_table(StringIO(df['ip'].to_csv(None,index=None)),sep='.')

为了比较,我使用 Marius 的代码并生成 20,000 ips:

import pandas as pd
import random
import numpy as np
from StringIO import StringIO

def make_ip():
    return '.'.join(str(random.randint(0, 255)) for n in range(4))

df = pd.DataFrame({'ip': [make_ip() for i in range(20000)]})

%timeit df[['ip0', 'ip1', 'ip2', 'ip3']] = df.ip.str.split('.', return_type='frame')
# 1 loops, best of 3: 3.06 s per loop

%timeit df[['ip0', 'ip1', 'ip2', 'ip3']] = df['ip'].apply(lambda x: pd.Series(x.split('.')))
# 1 loops, best of 3: 3.1 s per loop

%timeit df[['ip0', 'ip1', 'ip2', 'ip3']] = \
    pd.read_table(StringIO(df['ip'].to_csv(None,index=None)),sep='.',header=None)
# 10 loops, best of 3: 46.4 ms per loop

好的,所以我想将所有这些与仅使用 Python 列表和 Python 拆分进行比较,这应该比使用更高效的 Pandas 慢:

iplist = list(df['ip'])
%timeit [ x.split('.') for x in iplist ]
100 loops, best of 3: 10 ms per loop

什么!?显然,对大量字符串进行简单字符串操作的最佳方法是完全抛弃 Pandas使用 Pandas 会使这个过程慢 400 倍。但是,如果你想使用 Pandas,你也可以只转换为 Python 列表并返回:

%timeit df[['ip0', 'ip1', 'ip2', 'ip3']] = \
    pd.DataFrame([ x.split('.') for x in list(df['ip']) ])
# 100 loops, best of 3: 18.4 ms per loop

这里有一些非常错误。

【讨论】:

  • 其实可以走得更快;我正在搞乱一个快 2-3 倍的版本。这让 Pandas 字符串的性能看起来悲惨
  • 好吧,我们只需要一个 PR 来解决这个问题! github.com/pydata/pandas/issues/10081(请记住,您确实需要处理 NA;read_csv 确实可以处理)
  • @cge 你需要通过header=NoneFYI
  • 非常感谢。我想可能是因为数据帧在内存中不连续,所以查找内存地址需要大量时间
  • 不,问题已经在那个 github 链接上解决了,应该修复。它不是数据帧的基础。这只是 str_split 代码的一个简单问题。
猜你喜欢
  • 1970-01-01
  • 2011-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-04
  • 1970-01-01
相关资源
最近更新 更多