将一组字符串整数转换为整数数组的最有效方法

3
我有一个简单的问题 - 我需要将一串整数转换为整数列表,并将其插入到numpy数组中。
我有代码可以实现,但如果有更有效的方法,我很感兴趣。起始条件是我有一个由整数字符串组成的列表(第4行),目标是获得填充这些整数的numpy数组。
以下是我使用的代码示例:
import numpy as np
print("Hello StackOverflow")

listOfStringOfINTs = ["123231231231231"]*5
print(listOfStringOfINTs)
numpyVectorOfInts = np.empty([len(listOfStringOfINTs),len(listOfStringOfINTs[0]) ], dtype='int')
for i, IntString in enumerate(listOfStringOfINTs):
    numpyVectorOfInts[i] = list(map(int, IntString))

print(numpyVectorOfInts)

2
在Python中,使用驼峰命名法来命名变量不是一个好的习惯。应该使用“蛇形命名法”代替。 - Austin
谢谢你的建议,我会尽量在未来遵守它。 - artembus
驼峰命名法并不是“不好”的做法。在Python中,蛇形命名法只是首选的约定。我更喜欢驼峰命名法,所以我使用它,并且没有问题阅读函数和变量名称。 - tnknepp
@artembus 添加了一个向量化的解决方案来应对这种情况。 - Divakar
贴出的任一解决方案对您有用吗? - Divakar
显示剩余3条评论
5个回答

3

我不确定在速度方面是否更好,但这种方法更简单:

In [68]: np.array([list(astr) for astr in listOfStringOfINTs],int)           
Out[68]: 
array([[1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1],
       [1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1],
       [1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1],
       [1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1],
       [1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1]])

list(astr)将字符串拆分为一个由1个字符字符串组成的列表。使用int dtype的np.array将负责转换所有字符串。

或者您可以将所有字符串连接成一个字符串,创建列表,然后重新塑造数组:

np.array(list(''.join(listOfStringOfINTs)),int).reshape(5,-1)

2
利用所有字符串具有相同字符数的事实,我们可以使用矢量化的view。将Original Answer翻译为“最初的回答”。
def get_int_ar(a):
    return (np.array(a).view('u1')-48).reshape(len(a),-1)

最初的回答

示例运行 -

In [143]: listOfStringOfINTs = ["123231231231231"]*5

In [144]: get_int_ar(listOfStringOfINTs)
Out[144]: 
array([[1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1],
       [1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1],
       [1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1],
       [1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1],
       [1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1]], dtype=uint8)

1

仅仅是为了好玩,这里提供另一种实现方式:

>>> np.vstack(np.frombuffer(a,dtype=np.uint8)-48 for a in listOfStringOfINTs)
array([[1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1],
       [1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1],
       [1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1],
       [1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1],
       [1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1]], dtype=uint8)

这种方法依赖于将ASCII字符读入为无符号字符,然后依靠数字1-9在ASCII表示中的顺序。由于数字0表示为48,因此我们只需从所有值中减去48即可将它们作为整数获取其值。

对于小字符串而言,它并不比@hpaulj的方法更快,但@hpaulj的方法更易读:

In [1]: listOfStringOfINTs = ["123231231231231"]*10000

In [2]: %timeit np.vstack(np.frombuffer(a,dtype=np.uint8)-48 for a in listOfStringOfINTs)
10 loops, best of 3: 42.1 ms per loop

In [3]: %timeit np.array([list(astr) for astr in listOfStringOfINTs],int)
10 loops, best of 3: 36.3 ms per loop

但对于大字符串,它可以产生很大的影响:

In [4]: listOfStringOfINTs = ["123231231231231"*1000]*10000

In [5]: %timeit np.vstack(np.frombuffer(a,dtype=np.uint8)-48 for a in listOfStringOfINTs)
10 loops, best of 3: 115 ms per loop

In [6]: %timeit np.array([list(astr) for astr in listOfStringOfINTs],int)
1 loop, best of 3: 30.4 s per loop

1

以上所有答案都是正确的,但直观来说,对我来说最容易理解的是:

    >>> k = [list(x) for x in listOfStringOfINTs ]
    >>> print(np.array(k, dtype=np.int64))
    [[1 2 3 2 3 1 2 3 1 2 3 1 2 3 1]
     [1 2 3 2 3 1 2 3 1 2 3 1 2 3 1]
     [1 2 3 2 3 1 2 3 1 2 3 1 2 3 1]
     [1 2 3 2 3 1 2 3 1 2 3 1 2 3 1]
     [1 2 3 2 3 1 2 3 1 2 3 1 2 3 1]]

0

这里是使用"".join的解决方案:

def digit_ize(a):
    r = np.fromstring(''.join(a), 'u1')
    r &= 0x0f
    return r.reshape(len(a), -1)

或者(稍微快一点):

def digit_ize(a):
    r = np.frombuffer(''.join(a).encode(), 'u1') & 0x0f
    return r.reshape(len(a), -1)

时间:

small
pp1 4.314555088058114
pp2 2.933372976258397
div 3.740947926416993
usr 29.473979957401752
hpj 12.974489014595747
large
pp1 9.718517074361444
pp2 7.069707033224404
div 37.66830707900226
usr 2321.8201039126143
hpj 1103.1720889732242

生成计时的脚本,包含必要的 Py3 调整和其他解决方案。

import numpy as np

def digit_ize():
    r = np.fromstring(''.join(a), 'u1')
    r &= 0x0f
    return r.reshape(len(a), -1)

def digit_ize_2():
    r = np.frombuffer(''.join(a).encode(), 'u1') & 0x0f
    return r.reshape(len(a), -1)

def get_int_ar():
    return (np.array(a, 'S').view('u1')-48).reshape(len(a),-1)

def use_vstack():
    np.vstack(np.frombuffer(b.encode(), dtype=np.uint8)-48 for b in a)

def use_list():
    return np.array([list(astr) for astr in a],int)           

from timeit import timeit

listOfStringOfINTs = ["123231231231231"]*5
a = listOfStringOfINTs
print("small")
print("pp1", timeit(digit_ize, number=1000)*1000)
print("pp2", timeit(digit_ize_2, number=1000)*1000)
print("div", timeit(get_int_ar, number=1000)*1000)
print("usr", timeit(use_vstack, number=1000)*1000)
print("hpj", timeit(use_list, number=1000)*1000)
a = a*100
print("large")
print("pp1", timeit(digit_ize, number=1000)*1000)
print("pp2", timeit(digit_ize_2, number=1000)*1000)
print("div", timeit(get_int_ar, number=1000)*1000)
print("usr", timeit(use_vstack, number=1000)*1000)
print("hpj", timeit(use_list, number=1000)*1000)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接