如何将NumPy数组元素从字符串更改为int或float?

时间:2015-07-19 11:57:29

标签: python numpy

我有一个存储在NumPy数组中的数据集,如下所示,但其中的所有数据都存储为字符串。如何将字符串更改为intfloat,并将其存储在后面?

  data = numpy.array([]) # <--- array initialized with numpy.array

data变量中,存储以下信息

 [['1' '0' '3' ..., '7.25' '' 'S']
  ['2' '1' '1' ..., '71.2833' 'C85' 'C']
   ['3' '1' '3' ..., '7.925' '' 'S']
   ..., 
   ['889' '0' '3' ..., '23.45' '' 'S']
   ['890' '1' '1' ..., '30' 'C148' 'C']
   ['891' '0' '3' ..., '7.75' '' 'Q']]

我想将第一列更改为int并将值存储回来。为此,我做了:

 data[0::,0] = data[0::,0].astype(int)

但是,它并没有改变任何东西。

3 个答案:

答案 0 :(得分:2)

您可以在dtype初始化时设置数据类型(array)。例如,如果您的行由一个32位整数和一个4字节字符串组成,则可以指定dtype 'i4, S4'

data = np.array([(1, 'a'), (2, 'b')], dtype='i4, S4')

您可以阅读有关dtypes here的更多信息。

答案 1 :(得分:0)

NumPy数组的元素具有关联类型。分配给NumPy数组的切片会将新数据向上转换为该类型。如果不可能,则分配将失败,但例外情况为:

import numpy
a = numpy.array([[1, 2],[3, 4]])
print a
# [[1 2]
#  [3 4]]
print a.dtype
# int64

a[0,0] = 'look, a string'
# ValueError: invalid literal for long() with base 10: 'a'

在您的情况下,data[0::,0].astype(int)将生成一个具有关联成员类型int64的NumPy数组,但是将其分配回原始数组的切片会将它们转换回字符串。

除标准NumPy数组外,NumPy record arrays中提到的Padraic's comment允许不同列的不同类型。

我不知道标准的NumPy数组是否可以就地转换为NumPy 记录数组,因此构建一个像enrico's中建议的那样用

data = np.array([(1, 'a'), (2, 'b')], dtype='i4, S4')

可能是最好的选择。如果不可能,您可以从标准NumPy数组构造一个并用结果覆盖变量:

import numpy
data = numpy.array([['1', '0', '3', '7.25', '', 'S'],
                    ['2', '1', '1', '71.2833', 'C85', 'C'],
                    ['3', '1', '3', '7.925', '', 'S'],
                    ['889', '0', '3', '23.45', '', 'S'],
                    ['890', '1', '1', '30', 'C148', 'C'],
                    ['891', '0', '3', '7.75', '', 'Q']])
print(repr(data))
# array([['1', '0', '3', '7.25', '', 'S'],
#        ['2', '1', '1', '71.2833', 'C85', 'C'],
#        ['3', '1', '3', '7.925', '', 'S'],
#        ['889', '0', '3', '23.45', '', 'S'],
#        ['890', '1', '1', '30', 'C148', 'C'],
#        ['891', '0', '3', '7.75', '', 'Q']], 
#       dtype='|S7')

data = numpy.core.records.fromarrays(data.T, dtype='i4,S4,S4,S4,S4,S4')
print(repr(data))
# rec.array([(1, '0', '3', '7.25', '', 'S'), (2, '1', '1', '71.2', 'C85', 'C'),
#        (3, '1', '3', '7.92', '', 'S'), (889, '0', '3', '23.4', '', 'S'),
#        (890, '1', '1', '30', 'C148', 'C'), (891, '0', '3', '7.75', '', 'Q')], 
#       dtype=[('f0', '<i4'), ('f1', '|S4'), ('f2', '|S4'), ('f3', '|S4'), ('f4', '|S4'), ('f5', '|S4')])

答案 2 :(得分:0)

我可以通过从字符串列表开始来创建一个包含字符串的数组;请注意S4 dtype:

In [690]: data=np.array([['1','0','7.23','two'],['2','3','1.32','four']])

In [691]: data
Out[691]: 
array([['1', '0', '7.23', 'two'],
       ['2', '3', '1.32', 'four']], 
      dtype='|S4')

通过读取csv文件更有可能创建这样的数组。

我也可以将它视为单字节字符串数组 - 形状和dtype已更改,但数据缓冲区是相同的(相同的32字节)

In [692]: data.view('S1')
Out[692]: 
array([['1', '', '', '', '0', '', '', '', '7', '.', '2', '3', 't', 'w',
        'o', ''],
       ['2', '', '', '', '3', '', '', '', '1', '.', '3', '2', 'f', 'o',
        'u', 'r']], 
      dtype='|S1')

事实上,我可以更改单个字节,将原始数组的two更改为twos

In [693]: data.view('S1')[0,-1]='s'

In [694]: data
Out[694]: 
array([['1', '0', '7.23', 'twos'],
       ['2', '3', '1.32', 'four']], 
      dtype='|S4')

但是如果我尝试将data的元素更改为整数,则将其转换为与S4 dtype匹配的字符串:

In [695]: data[1,0]=4

In [696]: data
Out[696]: 
array([['1', '0', '7.23', 'twos'],
       ['4', '3', '1.32', 'four']], 
      dtype='|S4')

如果号码来自int(data[1,0])或其中的某些变体,则会发生同样的情况。

但是我可以欺骗它将整数看作一个字节串(表示为\x04

In [704]: data[1,0]=np.array(4).view('S4')

In [705]: data
Out[705]: 
array([['1', '0', '7.23', 'twos'],
       ['\x04', '3', '1.32', 'four']], 
      dtype='|S4')

数组可以共享数据缓冲区。 data属性是指向内存块的指针。数组的dtype控制着该块的解释方式。例如,我可以创建另一个int数组,并重定向它的data属性:

In [714]: d2=np.zeros((2,4),dtype=int)

In [715]: d2
Out[715]: 
array([[0, 0, 0, 0],
       [0, 0, 0, 0]])

In [716]: d2.data=data.data  # change the data pointer

In [717]: d2
Out[717]: 
array([[        49,         48,  858926647, 1936684916],
       [         4,         51,  842214961, 1920298854]])

现在d2[1,0]是整数4.但是其他项是无法识别的,因为它们是被视为整数的字符串。这与通过int()函数传递它们不同。

我不建议像这样更改data指针作为常规练习。把事情弄得很容易。我必须注意确保d2.nbytes为32,与data相同。

由于缓冲区已分片,d2中也会显示data的更改(但根据不同的dtype显示):

In [718]: d2[0,0]=3

In [719]: data
Out[719]: 
array([['\x03', '0', '7.23', 'twos'],
       ['\x04', '3', '1.32', 'four']], 
      dtype='|S4')

具有复杂dtype的视图执行类似的操作:

In [723]: data.view('i4,i4,f,|S4')
Out[723]: 
array([[(3, 48, 4.148588672592268e-08, 'twos')],
       [(4, 51, 1.042967401332362e-08, 'four')]], 
      dtype=[('f0', '<i4'), ('f1', '<i4'), ('f2', '<f4'), ('f3', 'S4')])

请注意同样出现在48中的51d2。下一个float列无法识别。

这给出了“就地”可以做什么和不可以做什么的想法。

但是要以有意义的方式获取包含数字和字符串的数组,我最好构造一个新的结构化数组。也许最简洁的方法是使用元组的中间列表。

In [759]: dl=[tuple(i) for i in data.tolist()]

In [760]: dl
Out[760]: [('1', '0', '7.23', 'two'), ('2', '3', '1.32', 'four')]

In [761]: np.array(dl,dtype='i4,i4,f,|S4')
Out[761]: 
array([(1, 0, 7.230000019073486, 'two'), (2, 3, 1.3200000524520874, 'four')], 
      dtype=[('f0', '<i4'), ('f1', '<i4'), ('f2', '<f4'), ('f3', 'S4')])

所有这些字段占用4个字节,因此nbytes是相同的。但个别价值已通过转换器。我给了'np.array'转换值的自由,这与输入和新dtype是一致的。这比尝试执行某种复杂的就地转换容易得多。

包含数字和字符串组合的列表元组也可以起作用:

[(1, 0, 7.23, 'two'), (2, 3, 1.32, 'four')]

结构化数组显示元组列表。在结构化数组docs中,值总是作为元组列表输入。

也可以使用

recarray,但实际上这只是一个数组子类,允许您将字段作为属性进行访问。

如果原始数组是从csv文件生成的,那么最好将np.genfromtxt(或loadtxt)与适当的选项一起使用。它可以生成适当的元组列表,并直接返回结构化数组。