如何对numpy数组进行字符串操作?
有没有比逐个遍历更好的方法来对 ndarray
进行字符串操作?我想用一种“向量化”的操作,但我只想到用 map
(示例已给出)或者列表推导式。
Arr = numpy.rec.fromrecords(zip(range(5),'as far as i know'.split()),
names='name, strings')
print ''.join(map(lambda x: x[0].upper()+'.',Arr['strings']))
=> A.F.A.I.K.
比如在R语言中,字符串操作也是向量化的:
> (string <- unlist(strsplit("as far as i know"," ")))
[1] "as" "far" "as" "i" "know"
> paste(sprintf("%s.",toupper(substr(string,1,1))),collapse="")
[1] "A.F.A.I.K."
2 个回答
是的,最近的NumPy增加了对字符串的向量化操作,这些功能在numpy.char
模块中可以找到。例如,当你想在一个字符串数组中找到所有以B开头的字符串时,可以这样做:
>>> y = np.asarray("B-PER O O B-LOC I-LOC O B-ORG".split())
>>> y
array(['B-PER', 'O', 'O', 'B-LOC', 'I-LOC', 'O', 'B-ORG'],
dtype='|S5')
>>> np.char.startswith(y, 'B')
array([ True, False, False, True, False, False, True], dtype=bool)
更新:请查看 Larsman 的回答,Numpy 最近添加了一个 numpy.char
模块,用于基本的字符串操作。
简短回答: Numpy 不提供向量化的字符串操作。通常的做法是这样做(假设 Arr
是你的 numpy 数组):
print '.'.join(item.upper() for item in Arr['strings'])
长回答,下面是为什么 numpy 不提供向量化字符串操作的原因:(中间还有一些闲聊)
数据结构没有“一刀切”的解决方案。
对于那些来自非特定领域编程语言的人来说,你的问题可能显得有些奇怪,但对于那些来自特定领域语言的人来说,这个问题就很有意义。
Python 提供了多种数据结构的选择。有些数据结构在某些任务上表现得比其他的更好。
首先,numpy 数组并不是 Python 中默认的“万用”容器。Python 内置的容器在它们设计的任务上表现得非常好。通常情况下,你会想用列表或字典。
Numpy 的 ndarray
适用于同质数据。
简单来说,numpy 没有向量化的字符串操作。
ndarray
是一种专门的容器,专注于以最小的内存存储 N 维的 同质 项目组。重点是尽量减少内存使用(我个人比较偏向这个,因为我主要就是为了这个目的使用它们,但这样想也很有用)。向量化的数学操作只是因为数据存储在连续的内存块中而产生的一个附带好处。
字符串通常长度不一。
例如 ['Dog', 'Cat', 'Horse']
。Numpy 采取了类似数据库的方式,要求你为字符串定义长度,但字符串长度不固定这一简单事实有很多影响。
大多数有用的字符串操作返回的是可变长度的字符串。(例如,你的例子中的 '.'.join(...)
)
那些不返回可变长度的操作(例如大写等),如果你想的话可以用其他操作来模拟。(例如,大写大致可以表示为 (x.view(np.uint8) - 32).view('S1')
。我不建议你这样做,但你可以……)
举个简单的例子:'A' + 'B'
结果是 'AB'
。'AB'
的长度与 'A'
或 'B'
都不一样。Numpy 处理其他类似的情况(例如 np.uint8(4) + np.float(3.4)
),但字符串的长度比数字要灵活得多。(数字的“上升转换”和“下降转换”规则相对简单。)
Numpy 不这样做的另一个原因是,它的重点在于 数值 操作。'A'**2
在 Python 中没有特别的定义(你当然可以创建一个有定义的字符串类,但应该是什么呢?)。字符串数组在 numpy 中是次要的存在。它们是存在的,但大多数操作并没有为它们定义。
Python 在处理字符串方面已经 非常 出色
Numpy 不尝试提供字符串操作的另一个(也是主要)原因是 Python 在这方面已经 非常 出色。
列表是非常灵活的容器。Python 有一整套非常好、非常快的字符串操作。列表推导式和生成器表达式相当快,而且它们不会因为尝试猜测返回项的类型或大小而增加任何开销,因为它们根本不在乎。(它们只是存储一个指向它的指针。)
此外,在 Python 中遍历 numpy 数组的速度比遍历列表或元组要慢,但对于字符串操作,最好还是使用普通的列表/生成器表达式。(例如,你的例子中的 print '.'.join(item.upper() for item in Arr['strings'])
)更好的是,根本不要用 numpy 数组来存储字符串。如果你有一个包含字符串的结构化数组的单列,那是可以理解的,但就仅此而已。Python 提供了 非常 丰富和灵活的数据结构。Numpy 数组并不是万能的,它们是一个特殊的情况,而不是一个通用的情况。
同时,请记住,你想用 numpy 数组做的大多数事情
学习 Python,而不仅仅是 Numpy
我不是想开玩笑,但使用 numpy 数组的方式与在 Matlab、R 或 IDL 等语言中的许多事情非常相似。
这是一个熟悉的范式,任何人的第一反应都是试图将这种范式应用到语言的其他部分。
Python 远不止于 numpy。它是一种多范式语言,因此很容易坚持你已经习惯的范式。尝试学习“用 Python 思考”,而不仅仅是“用 numpy 思考”。Numpy 为 Python 提供了一种特定的范式,但还有很多其他内容,有些范式在某些任务上比其他范式更合适。
这部分内容是熟悉不同数据容器(列表、字典、元组等)的优缺点,以及不同编程范式(例如面向对象、函数式、过程式等)。
总的来说,Python 有几种不同类型的专用数据结构。这使得它与像 R 或 Matlab 这样的特定领域语言有所不同,后者只有几种数据结构,但专注于用一种特定结构做所有事情。(我对 R 的经验有限,所以可能会错,但这就是我的印象。无论如何,Matlab 确实如此。)
无论如何,我不是想抱怨,但我花了很长时间才停止在 Matlab 中写 Fortran,甚至更久才停止在 Python 中写 Matlab。这段闲聊的回答缺乏具体的例子,但希望它至少能让你明白一点,并有所帮助。