将字符串拆分为列表并将项目转换为 int
Posted
技术标签:
【中文标题】将字符串拆分为列表并将项目转换为 int【英文标题】:Splitting a string into list and converting the items to int 【发布时间】:2019-03-25 05:09:47 【问题描述】:我有一个 pandas 数据框,其中有一个 values
列,如下所示:
0 16 0
1 7 1 2 0
2 5
3 1
4 18
我想要创建另一列modified_values
,其中包含拆分每个值后我将获得的所有不同数字的列表。新列将是这样的:
0 [16, 0]
1 [7, 1, 2, 0]
2 [5]
3 [1]
4 [18]
注意此列表中的值应为 int
而不是 strings
。
我知道的事情:
1) 我可以像这样以矢量化方式拆分列
df.values.str.split(" ")
。这会给我列表,但列表中的对象将是字符串。我可以在上面添加另一个操作,例如 df.values.str.split(" ").apply(func to convert values to int)
但这不会被矢量化
2) 我可以直接这样做df['modified_values']= df['values'].apply(func that splits as well as converts to int)
第二个肯定会比第一个慢得多,但我想知道是否可以通过矢量化方式实现相同的目标。
【问题讨论】:
【参考方案1】:不可能有原生的“矢量化”解决方案
我之所以强调这一点,是因为假设pd.Series.str
方法是矢量化的,这是一个常见的错误。他们不是。它们以效率为代价提供便利和错误处理。 仅适用于干净的数据,例如没有 NaN
值,列表理解可能是您的最佳选择:
df = pd.DataFrame('A': ['16 0', '7 1 2 0', '5', '1', '18'])
df['B'] = [list(map(int, i.split())) for i in df['A']]
print(df)
A B
0 16 0 [16, 0]
1 7 1 2 0 [7, 1, 2, 0]
2 5 [5]
3 1 [1]
4 18 [18]
性能基准测试
为了说明 pd.Series.str
的性能问题,您可以看到对于较大的数据帧,您传递给 Pandas 的操作越多,性能下降越多:
df = pd.concat([df]*10000)
%timeit [list(map(int, i.split())) for i in df['A']] # 55.6 ms
%timeit [list(map(int, i)) for i in df['A'].str.split()] # 80.2 ms
%timeit df['A'].str.split().apply(lambda x: list(map(int, x))) # 93.6 ms
list
作为 pd.Series
中的元素也是反熊猫
和described here一样,串联持有列表会产生2层指针,不推荐:
不要这样做。 Pandas 从来没有被设计成以系列/列的形式保存列表。您可以炮制昂贵的解决方法,但这些不是 推荐。
不推荐连续持有列表的主要原因是你输了 使用保存在连续内存块中的 NumPy 数组的矢量化功能。你的系列将是
object
dtype,表示指针序列,很像list
。你会输的 内存和性能方面的好处,以及访问优化的 Pandas 方法。另见What are the advantages of NumPy over regular Python lists? 支持 Pandas 的论点与支持 NumPy 的论点相同。
【讨论】:
很好地消除了.str
方法在这里很好的误解,但也许值得一提的是,由此产生的结构也并不真正符合 pandas 标准?
@roganjosh,是的,说得好。这需要复制粘贴编辑:)
@jpp 感谢关于str
的详细回答和急需的澄清
双 for
理解比 map
快。 Numba 比 NaN 快 250 倍。看我的回答。
@keiv.fly,很好的地方:嵌套列表组合!当然numba
结果在技术上是不同的(但实际上最好没有list
)。【参考方案2】:
双重for
理解比 jpp 答案中的 map
理解快 33%。 Numba 技巧比 jpp 的答案中的 map
理解要快 250 倍,但是您会得到一个带有浮点数和 nan
的 pandas DataFrame,而不是一系列列表。 Numba 包含在 Anaconda 中。
基准测试:
%timeit pd.DataFrame(nb_calc(df.A)) # numba trick 0.144 ms
%timeit [int(x) for i in df['A'] for x in i.split()] # 23.6 ms
%timeit [list(map(int, i.split())) for i in df['A']] # 35.6 ms
%timeit [list(map(int, i)) for i in df['A'].str.split()] # 50.9 ms
%timeit df['A'].str.split().apply(lambda x: list(map(int, x))) # 56.6 ms
Numba 函数代码:
@numba.jit(nopython=True, nogil=True)
def str2int_nb(nb_a):
n1 = nb_a.shape[0]
n2 = nb_a.shape[1]
res = np.empty(nb_a.shape)
res[:] = np.nan
j_res_max = 0
for i in range(n1):
j_res = 0
s = 0
for j in range(n2):
x = nb_a[i,j]
if x == 32:
res[i,j_res]=np.float64(s)
s=0
j_res+=1
elif x == 0:
break
else:
s=s*10+x-48
res[i,j_res]=np.float64(s)
if j_res>j_res_max:
j_res_max = j_res
return res[:,:j_res_max+1]
def nb_calc(s):
a_temp = s_a.values.astype("U")
nb_a = a_temp.view("uint32").reshape(len(s_a),-1).astype(np.int8)
str2int_nb(nb_a)
Numba 不支持字符串。所以我首先转换为 int8 数组,然后才使用它。转换为 int8 实际上需要 3/4 的执行时间。
我的 numba 函数的输出如下所示:
0 1 2 3
-----------------------
0 16.0 0.0 NaN NaN
1 7.0 1.0 2.0 0.0
2 5.0 NaN NaN NaN
3 1.0 NaN NaN NaN
4 18.0 NaN NaN NaN
【讨论】:
以上是关于将字符串拆分为列表并将项目转换为 int的主要内容,如果未能解决你的问题,请参考以下文章