规范化/标准化一个numpy结构数组

7

我想知道规范化/标准化numpy recarray 的最佳方法。 需要明确的是,我不是在谈论数学矩阵,而是一个记录数组,其中还包括文本列(例如标签)。

a = np.genfromtxt("iris.csv", delimiter=",", dtype=None)
print a.shape
> (150,)

正如你所看到的,我无法处理 a[:,:-1] 这样的形状是一维的内容。

我找到的最好方法是遍历所有列:

for nam in a.dtype.names[:-1]:
    col = a[nam]
    a[nam] = (col - col.min()) / (col.max() - col.min())

有更加优雅的方式来实现这个吗?是否有一些方法,比如“normalize”或“standardize”之类的方法?
2个回答

7
有许多方法可以做到这一点,但有些比其他方法更加清晰。通常,在numpy中,您将字符串数据保存在单独的数组中。
(事情比如R的数据框架更低级一些。您通常只需将其封装在一个类中以进行关联,但保持不同的数据类型分开。)
老实说,numpy并没有针对处理此类“灵活”数据类型进行优化(尽管它肯定可以做到)。像pandas这样的东西为“类似电子表格”的数据提供了更好的接口(而pandas只是numpy的一层)。
但是,结构化数组(就像您在这里拥有的那样)将允许您在传入字段名称列表时按列切片它们。(例如data[['col1','col2','col3']]
无论如何,一种方法是像这样做:
import numpy as np

data = np.recfromcsv('iris.csv')

# In this case, it's just all but the last, but we could be more general
# This must be a list and not a tuple, though.
float_fields = list(data.dtype.names[:-1])

float_dat = data[float_fields]

# Now we just need to view it as a "regular" 2D array...
float_dat = float_dat.view(np.float).reshape((data.size, -1))

# And we can normalize columns as usual.
normalized = (float_dat - float_dat.min(axis=0)) / float_dat.ptp(axis=0)

然而,这远非理想。如果您想要原地进行操作(如当前所做),最简单的解决方案就是遍历字段名称。顺便提一下,使用 pandas,您可以这样做:
import pandas
data = pandas.read_csv('iris.csv', header=None)

float_dat = data[data.columns[:-1]]
dmin, dmax = float_dat.min(axis=0), float_dat.max(axis=0)

data[data.columns[:-1]] = (float_dat - dmin) / (dmax - dmin)

1
+1 谢谢。这是非常有见地和信息量丰富的答案。将数据集分成数值和非数值列可能是正确的方法。这样可以使许多其他操作变得明确,并且实际上也是我尝试做的事情。我不知道使用 data[list] 来选择多个列的选项。 - Has QUIT--Anony-Mousse

1
你使用的NumPy版本是什么?在1.5.1版本中,我没有遇到这种情况。我制作了一个简短的文本文件作为示例,保存为test.txt
last,first,country,state,zip
tyson,mike,USA,Nevada,89146
brady,tom,USA,Massachusetts,02035

当我执行以下代码时,我得到了这个结果:
>>> import numpy as np
>>> a = np.genfromtxt("/home/ely/Desktop/Python/test.txt",delimiter=',',dtype=None)
>>> print a.shape
(3,5)
>>> print a
[['last' 'first' 'country' 'state' 'zip']
 ['tyson' 'mike' 'USA' 'Nevada' '89146']
 ['brady' 'tom' 'USA' 'Massachusetts' '02035']]
>>> print a[0,:-1]
['last' 'first' 'country' 'state']
>>> print a.dtype.names
None

我只是想知道你的数据有什么不同。

注意:这只是一个注释,而不是答案...只是需要更多的空间来放置上面的示例。 - ely
1
不同的是,你得到的是一个字符串数组,而不是一个结构化数组。请查看你示例中 a 的数据类型。 - Joe Kington
当然,但是是什么导致传入的数组被“结构化”?如果它只是一个csv文件,那么genfromtxt()不会总是产生一个字符串数组吗? - ely
不是的。在你的情况下,它只创建了一个字符串数组,因为第一行(列名)都是字符串。如果第一行中有任何列中包含数字,则会得到一个结构化数组。在你的情况下,如果指定 names=True,则将从第一行读取列名,并且您将获得一个结构化数组。(顺便说一下,这里所说的“结构化”请参见此处 - Joe Kington
@JoeKington可能已经明白了区别。我有四列纯数字,想要进行标准化,还有一列带有标签。 - Has QUIT--Anony-Mousse

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