我有一个熊猫数据框,其中有一个像这样的列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)
第二个肯定会比第一个慢很多,但是我想知道是否可以通过向量化的方式实现相同的目的。
答案 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)