【问题标题】:Delete numpy column by name按名称删除numpy列
【发布时间】:2017-07-03 07:25:43
【问题描述】:

我通过 csv 创建了一个 numpy 数组

dtest = np.genfromtxt('data/test.csv', delimiter=",", names = True)

数据有 200 列,名为“name”、“id”等。 我正在尝试删除“id”列。

我可以使用列名吗?

【问题讨论】:

标签: python numpy


【解决方案1】:

建议副本中的答案How do you remove a column from a structured numpy array? 展示如何引用结构化数组的字段子集。这可能是您想要的,但它有一个潜在的问题,我将稍作说明。

从一个小样本 csv 'file' 开始:

In [32]: txt=b"""a,id,b,c,d,e
    ...: a1, 3, 0,0,0,0.1
    ...: b2, 4, 1,2,3,4.4
    ...: """
In [33]: data=np.genfromtxt(txt.splitlines(), delimiter=',',names=True, dtype=None)
In [34]: data
Out[34]: 
array([(b'a1', 3, 0, 0, 0,  0.1), 
       (b'b2', 4, 1, 2, 3,  4.4)], 
      dtype=[('a', 'S2'), ('id', '<i4'), ('b', '<i4'), ('c', '<i4'), ('d', '<i4'), ('e', '<f8')])

多字段选择

我可以通过字段名称列表获取字段子集的“视图”。 “副本”展示了如何从data.dtype.names 构建这样的列表。在这里我只输入它,省略“id”名称。

In [35]: subd=data[['a','b','c','d']]
In [36]: subd
Out[36]: 
array([(b'a1', 0, 0, 0), (b'b2', 1, 2, 3)], 
      dtype=[('a', 'S2'), ('b', '<i4'), ('c', '<i4'), ('d', '<i4')])

问题在于这不是常规的“视图”。可以阅读,但任何写入子集的尝试都会引发警告。

In [37]: subd[0]['b'] = 3
/usr/local/bin/ipython3:1: FutureWarning: Numpy has detected that you (may be) writing to an array returned
by numpy.diagonal or by selecting multiple fields in a structured
array. This code will likely break in a future numpy release --
see numpy.diagonal or arrays.indexing reference docs for details.
The quick fix is to make an explicit copy (e.g., do
arr.diagonal().copy() or arr[['f0','f1']].copy()).
  #!/usr/bin/python3

制作子集副本是可以的。但对subd 的更改不会影响data

In [38]: subd=data[['a','b','c','d']].copy()
In [39]: subd[0]['b'] = 3
In [40]: subd
Out[40]: 
array([(b'a1', 3, 0, 0), (b'b2', 1, 2, 3)], 
      dtype=[('a', 'S2'), ('b', '<i4'), ('c', '<i4'), ('d', '<i4')])

从索引列表中删除ith字段名的简单方法:

In [60]: subnames = list(data.dtype.names)   # list so its mutable
In [61]: subnames
Out[61]: ['a', 'id', 'b', 'c', 'd', 'e']
In [62]: del subnames[1]

usecols

由于您正在从csv 读取此数组,因此您可以使用usecols 加载除“id”列之外的所有内容

由于您有大量列,因此可以轻松执行以下操作:

In [42]: col=list(range(6)); del col[1]
In [43]: col
Out[43]: [0, 2, 3, 4, 5]
In [44]: np.genfromtxt(txt.splitlines(), delimiter=',',names=True, dtype=None,usecols=col)
Out[44]: 
array([(b'a1', 0, 0, 0,  0.1), (b'b2', 1, 2, 3,  4.4)], 
      dtype=[('a', 'S2'), ('b', '<i4'), ('c', '<i4'), ('d', '<i4'), ('e', '<f8')])

recfunctions

有一个函数库可以帮助操作结构化数组

In [45]: import numpy.lib.recfunctions as rf
In [47]: rf.drop_fields(data, ['id'])
Out[47]: 
array([(b'a1', 0, 0, 0,  0.1), (b'b2', 1, 2, 3,  4.4)], 
      dtype=[('a', 'S2'), ('b', '<i4'), ('c', '<i4'), ('d', '<i4'), ('e', '<f8')])

该组中的大多数函数通过使用目标 dtype 构造一个“空白”数组来工作,然后按字段将值从源复制到目标。

字段复制

这是recfunctions中使用的字段复制方法:

In [65]: data.dtype.descr  # dtype description as list of tuples
Out[65]: 
[('a', '|S2'),
 ('id', '<i4'),
 ('b', '<i4'),
 ('c', '<i4'),
 ('d', '<i4'),
 ('e', '<f8')]
In [66]: desc=data.dtype.descr
In [67]: del desc[1]                # remove one field
In [68]: res = np.zeros(data.shape, dtype=desc)  # target
In [69]: res
Out[69]: 
array([(b'', 0, 0, 0,  0.), (b'', 0, 0, 0,  0.)], 
      dtype=[('a', 'S2'), ('b', '<i4'), ('c', '<i4'), ('d', '<i4'), ('e', '<f8')])
In [70]: for name in res.dtype.names:    # copy by field name
    ...:     res[name] = data[name]

In [71]: res
Out[71]: 
array([(b'a1', 0, 0, 0,  0.1), (b'b2', 1, 2, 3,  4.4)], 
      dtype=[('a', 'S2'), ('b', '<i4'), ('c', '<i4'), ('d', '<i4'), ('e', '<f8')])

由于通常结构化数组记录多,字段少,所以按字段名复制比较快。

链接的 SO 引用了 matplotlib.mlab.rec_drop_fields(rec, names)。这基本上完成了我刚刚概述的操作 - 使用所需字段创建目标,并按名称复制字段。

newdtype = np.dtype([(name, rec.dtype[name]) for name in rec.dtype.names
                     if name not in names])

【讨论】:

  • 在你的 usecols` 标题下,如果你不知道 id 在哪一列,你会怎么做?我真的很喜欢你的refunctions 信息顺便说一句。很好的信息丰富的答案。
  • 我必须进行探索性阅读(例如,行数有限的 genfromtxt)才能找到名称。我可能错了,但我想大多数 csv 用户事先知道列名是什么。
  • 自编写以来,多字段索引已更改。阅读结构化数组文档了解详细信息。
【解决方案2】:

我知道你有一个全面的答案,但这是我刚刚整理的另一个答案。

import numpy as np

对于一些示例数据文件:

test1.csv = 
a   b   c   id
0   1   2   3
4   5   6   7
8   9   10  11

使用genfromtxt导入:

d = np.genfromtxt('test1.csv', delimiter="\t", names = True)

d
> array([(0.0, 1.0, 2.0, 3.0), (4.0, 5.0, 6.0, 7.0), (8.0, 9.0, 10.0, 11.0)], 
  dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8'), ('id', '<f8')])

通过执行以下操作从数组中返回单列:

d['a']
> array([ 0.,  4.,  8.])

要删除名为“id”的列,您可以执行以下操作:

通过写入返回列名列表:

list(d.dtype.names)
> ['a', 'b', 'c', 'id']

通过仅返回不等于字符串 id 的列来创建一个新的 numpy 数组。

使用列表推导返回一个不带“id”字符串的新列表:

[b for b in list(d.dtype.names) if b != 'id']
> ['a', 'b', 'c']

结合给予:

d_new = d[[b for b in list(d.dtype.names) if b != 'id']]

> array([(0.0, 1.0, 2.0), (4.0, 5.0, 6.0), (8.0, 9.0, 10.0)], 
  dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])

这会返回数组:

a   b   c
0   1   2
4   5   6
8   9   10

【讨论】:

    【解决方案3】:

    这可能是numpy 中的新功能(适用于 1.20.2),但您可以使用名称列表对命名数组进行切片(但名称元组不起作用)。

    data = np.genfromtxt('some_file.csv', names=['a', 'b', 'c', 'd', 'e'])
    # I don't want colums b or d
    sliced = data[['a', 'c', 'd']]
    

    我注意到您需要删除许多名为 id 的列。这些列在被genfromtxt 解析时显示为['id', 'id_1', 'id_2', ...],依此类推,因此您可以使用一些列表推导来挑选这些列名称并从中分割出来。

    no_ids = data[[n for n in data.dtype.names if 'id' not in n]]
    

    【讨论】:

    • 2017 年存在多字段索引 - 请参阅我的答案。实施细节已更改。
    猜你喜欢
    • 2014-08-31
    • 2011-06-04
    • 1970-01-01
    • 2012-05-22
    相关资源
    最近更新 更多