在列上使用 `split` 太慢了 - 我怎样才能获得更好的性能?
Using `split` on columns too slow - how can I get better performance?
我有一个通话记录数据集(大约 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 个小时...
有什么更好的方法吗?
示例数据(如果您提供此数据,您的问题更有可能得到解答):
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
原来Pandas中的str.split
(在core/strings.py
中为str_split
)其实很慢;它没有任何效率提高,并且仍然通过使用 Python 进行迭代,没有提供任何加速。
实际上,请参见下文。 Pandas这方面的表现简直惨不忍睹;这不仅仅是 Python vs C 迭代,因为用 Python 列表做同样的事情是最快的方法!
不过,有趣的是,有一个更快的技巧解决方案:将系列写成文本,然后使用“.”再次读入。作为分隔符:
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
这里有一些非常的错误。
我有一个通话记录数据集(大约 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 个小时...
有什么更好的方法吗?
示例数据(如果您提供此数据,您的问题更有可能得到解答):
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
原来Pandas中的str.split
(在core/strings.py
中为str_split
)其实很慢;它没有任何效率提高,并且仍然通过使用 Python 进行迭代,没有提供任何加速。
实际上,请参见下文。 Pandas这方面的表现简直惨不忍睹;这不仅仅是 Python vs C 迭代,因为用 Python 列表做同样的事情是最快的方法!
不过,有趣的是,有一个更快的技巧解决方案:将系列写成文本,然后使用“.”再次读入。作为分隔符:
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
这里有一些非常的错误。