将字符串拆分为列表并将项目转换为int

时间:2018-10-20 15:13:46

标签: python python-3.x pandas numpy

我有一个熊猫数据框,其中有一个像这样的列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)

第二个肯定会比第一个慢很多,但是我想知道是否可以通过向量化的方式实现相同的目的。

2 个答案:

答案 0 :(得分:6)

不可能使用本地的“矢量化”解决方案

我强调这一点是因为假设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层指针,不建议:

  

不要这样做。熊猫从来没有被设计成容纳系列/专栏的列表。您可以炮制昂贵的解决方法,但这不是   推荐。

     

不推荐连续举牌的主要原因是输了   使用连续内存块中保存的NumPy数组的矢量化功能。您的系列将是   object dtype,它表示一系列指针,与list类似。你会输的   在内存和性能以及访问优化的Pandas方法方面都有好处。

     

另请参阅What are the advantages of NumPy over regular Python lists?   支持熊猫的论点与针对NumPy的论点相同。

答案 1 :(得分:1)

根据jpp的回答,双^\/(?!https:\/\/domain\/#\/#\/*\/profile\/) 理解比for理解快33%。 Numba技巧比jpp的答案对map的理解要快250倍,但是您会得到一个带有浮点数和map的pandas DataFrame,而不是一系列列表。 Numba包含在Anaconda中。

基准:

nan

Numba函数的代码:

%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不支持字符串。所以我首先转换为int8数组,然后再使用它。转换为int8实际上需要执行时间的3/4。

我的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)