【问题标题】:Python Import Text Array with NumpyPython 使用 Numpy 导入文本数组
【发布时间】:2016-08-30 14:10:19
【问题描述】:

我有一个如下所示的文本文件:

...
5   [0, 1]  [512, 479]  991
10  [1, 0]  [706, 280]  986
15  [1, 0]  [807, 175]  982
20  [1, 0]  [895, 92]   987
...

每一列都是制表符分隔的,但有些列中有数组。我可以用np.genfromtxt 以某种方式导入这些吗?

生成的解包列表应该是,例如:

data1 = [..., 5, 10, 15, 20, ...]
data2 = [..., [512, 479], [706, 280], ... ] (i.e. a 2D list)
etc.

我试过了

data1, data2, data3, data4 = np.genfromtxt('data.txt', dtype=None, delimiter='\t', unpack=True)

data2data3 是包含“nan”的列表。

【问题讨论】:

  • 如果 genfromttxt 不起作用,请尝试其他方法,例如遍历行并构建 numpy 可以用来创建数组的列表。
  • 你可以使用 genfromtxt 的usecolsconverters 参数
  • 括号使使用股票 txt 加载程序更加困难。您是否尝试过逐行读取文件并自己解析每一行?
  • 我现在有这样的东西,基于将文本文件导入一个大“数据”对象并解析创建的字符串:datastr = data[i][1][1:-1].split(',') dataarray = [] for j in range(0, len(datastr)): dataarray.append(int(datastr[j])) data2.append(dataarray) 它可以工作,但看起来很笨重。
  • 我认为genfromtxt converter 不能用于将一列分成两列。

标签: python arrays numpy genfromtxt


【解决方案1】:

给定数据的潜在方法,但不使用 numpy:

import ast

data1, data2, data3, data4 = [],[],[],[]

for l in open('data.txt'):
    data = l.split('\t')

    data1.append(int(data[0]))
    data2.append(ast.literal_eval(data[1]))
    data3.append(ast.literal_eval(data[2]))
    data4.append(int(data[3]))

print 'data1', data1
print 'data2', data2
print 'data3', data3
print 'data4', data4

给予

"data1 [5, 10, 15, 20]"
"data2 [[0, 1], [1, 0], [1, 0], [1, 0]]"
"data3 [[512, 479], [706, 280], [807, 175], [895, 92]]"
"data4 [991, 986, 982, 987]"

【讨论】:

  • 那些datan 列表最后可以很容易地变成数组。因此,执行此操作不会损失速度或功能。
【解决方案2】:

csv 文件中的括号不管你怎么看都是笨拙的。默认的 csv 结构是 2d - 行和统一的列。括号添加了嵌套级别。但是,列是制表符分隔,而嵌套块是逗号分隔的事实使它更容易一些。

您的评论代码是(添加了换行符)

datastr = data[i][1][1:-1].split(',') 
dataarray = [] 
for j in range(0, len(datastr)): 
     dataarray.append(int(datastr[j])) 
data2.append(dataarray)

我假设data[i] 看起来像(在标签拆分之后):

['5', '[0, 1]', '[512, 479]',  '991']

因此,对于“[0,1]”,您可以去掉 [],拆分其余部分,然后将该列表放回 data2

这看起来确实是一种可行的方法。 genfromtxt 确实处理括号或引号。 csv 阅读器可以处理带引号的文本,并且可能适合将 [] 视为引号。但除此之外,我认为 '[]` 必须像您一样通过某种字符串处理来处理。

请记住,genfromtxt 只是读取行、解析它们,然后将结果列表收集到主列表中。然后它在最后将该列表转换为数组。所以自己做一行一行,一串一串的解析也不逊色。

=============

将您的示例作为文本文件:

 In [173]: txt=b"""
 ...: 5  \t [0, 1] \t [512, 479] \t 991
 ...: 10 \t [1, 0] \t [706, 280] \t 986
 ...: 15 \t [1, 0] \t [807, 175] \t 982
 ...: 20 \t [1, 0] \t [895, 92]  \t 987"""

一个简单的genfromtxt 调用dtype=None

In [186]: data = np.genfromtxt(txt.splitlines(), dtype=None, delimiter='\t', autostrip=True)

结果是一个包含整数和字符串字段的结构化数组:

In [187]: data
Out[187]: 
array([(5, b'[0, 1]', b'[512, 479]', 991),
       (10, b'[1, 0]', b'[706, 280]', 986),
       (15, b'[1, 0]', b'[807, 175]', 982),
       (20, b'[1, 0]', b'[895, 92]', 987)], 
      dtype=[('f0', '<i4'), ('f1', 'S6'), ('f2', 'S10'), ('f3', '<i4')])

字段按名称访问

In [188]: data['f0']
Out[188]: array([ 5, 10, 15, 20])
In [189]: data['f1']
Out[189]: 
array([b'[0, 1]', b'[1, 0]', b'[1, 0]', b'[1, 0]'], 
      dtype='|S6')

如果我们可以处理[],您的数据可以很好地表示为具有复合数据类型的结构化数组

In [191]: dt=np.dtype('i,2i,2i,i')
In [192]: np.ones((3,),dtype=dt)
Out[192]: 
array([(1, [1, 1], [1, 1], 1), (1, [1, 1], [1, 1], 1),
       (1, [1, 1], [1, 1], 1)], 
      dtype=[('f0', '<i4'), ('f1', '<i4', (2,)), ('f2', '<i4', (2,)), ('f3', '<i4')])

'f1' 字段是一个 (3,2) 数组。

一种方法是通过过滤掉多余字符的函数传递文本/文件。 genfromtxt 适用于任何可以一次输入一行的内容。

def afilter(txt):
    for line in txt.splitlines():
        line=line.replace(b'[', b' ').replace(b']', b'').replace(b',' ,b'\t')
        yield line

这个生成器去掉 [] 并用制表符替换 , 实际上生成一个平面 csv 文件

In [205]: list(afilter(txt))
Out[205]: 
[b'',
 b'5  \t  0\t 1  \t  512\t 479  \t 991',
 b'10 \t  1\t 0  \t  706\t 280  \t 986',
 b'15 \t  1\t 0  \t  807\t 175  \t 982',
 b'20 \t  1\t 0  \t  895\t 92   \t 987']

genfromtxtdtype=None 将生成一个包含 6 列的数组。

In [209]: data=np.genfromtxt(afilter(txt),delimiter='\t',dtype=None)
In [210]: data
Out[210]: 
array([[  5,   0,   1, 512, 479, 991],
       [ 10,   1,   0, 706, 280, 986],
       [ 15,   1,   0, 807, 175, 982],
       [ 20,   1,   0, 895,  92, 987]])
In [211]: data.shape
Out[211]: (4, 6)

但如果我给它上面定义的dt dtype,我会得到一个结构化数组:

In [206]: data=np.genfromtxt(afilter(txt),delimiter='\t',dtype=dt)
In [207]: data
Out[207]: 
array([(5, [0, 1], [512, 479], 991), (10, [1, 0], [706, 280], 986),
       (15, [1, 0], [807, 175], 982), (20, [1, 0], [895, 92], 987)], 
      dtype=[('f0', '<i4'), ('f1', '<i4', (2,)), ('f2', '<i4', (2,)), ('f3', '<i4')])
In [208]: data['f1']
Out[208]: 
array([[0, 1],
       [1, 0],
       [1, 0],
       [1, 0]], dtype=int32)

括号可以处理多个级别。我不认为一个比另一个有很多优势。

【讨论】:

    【解决方案3】:

    作为genfromtxt 的替代品,您可以尝试fromregex。您基本上在正则表达式组和 numpy 结构化 dtype 的字段之间建立了类比。

    在这个例子中,我解析出所有数字,而不用担心它们是单个数字还是数组。然后我切换到指定哪些列有数组的 dtype。

    import numpy as np
    
    # regular expression that will extract 6 numbers from each line
    re = 6 * r"(\d+)\D*"
    
    # dtype for a single number
    dt_num = np.int
    
    # structured dtype of 6 numbers
    dt = 6 * [('', dt_num)]
    
    # parse the file
    a = np.fromregex("data.txt", re, dt)
    
    # change to a more descriptive structured dtype
    a.dtype = [
      ('data1', dt_num),
      ('data2', dt_num, (2,)),
      ('data3', dt_num, (2,)),
      ('data4', dt_num)
    ]
    
    print(a['data1'])
    print(a['data2'])
    print(a['data3'])
    print(a['data4'])
    

    切换 numpy 数组的 dtype 的好处是它不必处理或制作数据的新副本,它只是重新解释您在访问数据时获得的内容。

    此解决方案的缺点之一是构建复杂的正则表达式和构建结构化 dtype 会变得很丑陋。在这种情况下,您必须使两者保持同步。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-20
      • 2012-02-09
      • 1970-01-01
      • 2018-01-02
      • 2015-06-08
      • 2018-11-30
      • 2017-12-23
      • 1970-01-01
      相关资源
      最近更新 更多