将字符串拆分为列表并将项目转换为 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的主要内容,如果未能解决你的问题,请参考以下文章

拆分字符串,将 ToList<int>() 转换为一行

如何将字符串拆分为列表?

将字符串拆分为标记并将标记分成两个单独的数组

将字符串列表拆分为地图列表

Arduino进行串行读取字符串拆分然后转换为Int

如何在 Ruby 中拆分分隔字符串并将其转换为数组?