【问题标题】:Python Reading (floating-point) values one at a timePython 一次读取(浮点)值
【发布时间】:2017-04-09 13:10:42
【问题描述】:

我想用来自文件的一些浮点值填充一个 numpy 数组。数据会这样存储:

0 11
5 6.2 4 6
2 5 3.2 6
7 1.4 5 11

第一行给出第一个和最后一个索引,接下来的行是实际数据。我目前的方法是拆分每条数据线,在每个部分上使用float,并将值逐片存储在预先分配的数组中。这是我现在的做法:

data_file ='data.txt'
# Non needed stuff at the beginning
skip_lines = 0

with open(data_file, 'r') as f:
    # Skip any lines if needed
    for _ in range(skip_lines):
        f.readline()
    # Get the data size and preallocate the numpy array
    first, last = map(int, f.readline().split())
    size = last - first + 1
    data = np.zeros(size)

    beg, end = (-1, 0) # Keep track of where to fill the array
    for line in f:
        if end - 1 == last:
            break
        samples = line.split()
        beg = end
        end += len(samples)
        data[beg:end] = [float(s) for s in samples]

有没有办法在 Python 中一个一个地读取数据值?

import numpy as np
f = open('data.txt', 'r')
first, last = map(int, f.readline().split())
arr = np.zeros(last - first + 1)
for k in range(last - first + 1):
    data = f.read() # This does not work. Any idea?
    # In C++, it could be done this way: double data; cin >> data
    arr[k] = data

编辑唯一可以确定的是,前两个数字是第一个和最后一个索引,并且最后一个数据行只有最后一个数字。在数据编号之后可以有其他的东西。所以不能只读取“第一行,最后一行”之后的所有行。

编辑 2 添加(工作)初始方法(拆分每个数据行,在每个部分上使用 float,并将值存储在预先分配的数组中,逐个切片)实现。

【问题讨论】:

  • 我们是否应该将第四行末尾的11 作为一种收集结束标记?
  • 你的数据集有多大?
  • @BillBell 11 是最后一个条目的索引。目前,它仅用于计算数据大小(我见过的所有示例文件都有 0 作为第一个条目,但它可能会改变)。文件一般不大,都可以读入内存。典型的first, last0, 1023

标签: python file numpy input


【解决方案1】:

由于您的样本在每行中的列数相同(第一行除外),我们可以将其读取为csv,例如loadtxt

In [1]: cat stack43307063.txt
0 11
5 6.2 4 6
2 5 3.2 6
7 1.4 5 11
In [2]: arr = np.loadtxt('stack43307063.txt', skiprows=1)
In [3]: arr
Out[3]: 
array([[  5. ,   6.2,   4. ,   6. ],
       [  2. ,   5. ,   3.2,   6. ],
       [  7. ,   1.4,   5. ,  11. ]])

这很容易重塑和操纵。如果列不一致,则需要逐行处理。

In [9]: alist = []
In [10]: with open('stack43307063.txt') as f:
    ...:     start, stop = [int(i) for i in f.readline().split()]
    ...:     print(start, stop)
    ...:     for line in f: # f.readline()
    ...:         print(line.split())
    ...:         alist.append([float(i) for i in line.split()])
    ...:         
0 11
['5', '6.2', '4', '6']
['2', '5', '3.2', '6']
['7', '1.4', '5', '11']
In [11]: alist
Out[11]: [[5.0, 6.2, 4.0, 6.0], [2.0, 5.0, 3.2, 6.0], [7.0, 1.4, 5.0, 11.0]]

append 替换为extend 以收集平面列表中的值:

alist.extend([float(i) for i in line.split()])
[5.0, 6.2, 4.0, 6.0, 2.0, 5.0, 3.2, 6.0, 7.0, 1.4, 5.0, 11.0]

c++ io 通常使用流。使用 Python 可以进行流式传输,但文本文件更常见的是逐行读取。

In [15]: lines = open('stack43307063.txt').readlines()
In [16]: lines
Out[16]: ['0 11\n', '5 6.2 4 6\n', '2 5 3.2 6\n', '7 1.4 5 11\n']

可以按上述方式处理的行列表。

fromfile 也可以使用,除了它会丢失原始中的任何行/列结构:

In [20]: np.fromfile('stack43307063.txt',sep=' ')
Out[20]: 
array([  0. ,  11. ,   5. ,   6.2,   4. ,   6. ,   2. ,   5. ,   3.2,
         6. ,   7. ,   1.4,   5. ,  11. ])

此负载包括第一行。我们可以使用 open 和 readline 跳过它。

In [21]: with open('stack43307063.txt') as f:
    ...:     start, stop = [int(i) for i in f.readline().split()]
    ...:     print(start, stop)
    ...:     arr = np.fromfile(f, sep=' ')        
0 11
In [22]: arr
Out[22]: 
array([  5. ,   6.2,   4. ,   6. ,   2. ,   5. ,   3.2,   6. ,   7. ,
         1.4,   5. ,  11. ])

fromfile 也有一个count 参数,可以从你的startstop 中设置。但除非您只想阅读子集,否则不需要它。

【讨论】:

  • 文件格式对列宽不是很清楚,所以我们必须假设列不一致。我的示例文件每行有 10 个数字,但我不想一概而论。唯一可以确定的是,前两个数字是第一个和最后一个索引,并且最后一个数据行只有最后一个数字。数据编号后面还可以有其他东西。
  • 带有列表扩展的 readline 为您提供了最大的控制权。如果一行中有非数字,则必须将其过滤掉。
  • 只要从first条目读取到last条目,就不能连续出现非数字。但是,从包含 last 数字的行之后的行开始可能还有其他内容
  • 然后我将readline 放在一个while循环中 - 读取直到列表足够长然后中断。
  • 所以你的意思是,首先在while循环中构建整个数字列表?您的建议是否接近我最初的方法(请参阅我的编辑,我进行了实施)还是有所不同?您介意在答案中添加它吗?
【解决方案2】:

仅假设前两个数字表示后面数字中所需值的索引。不同数量的数字可以出现在第一行或后续行中。不会读取 last 以外的令牌。

from io import StringIO
sample = StringIO('''3 11 5\n 6.2 4\n6 2 5 3.2 6 7\n1.4 5 11''')
from shlex import shlex
lexer = shlex(instream=sample, posix=False)
lexer.wordchars = r'0123456789.'
lexer.whitespace = ' \n'
lexer.whitespace_split = True

def oneToken():
    while True:
        token = lexer.get_token()
        if token:
            token = token.strip()
            if not token:
                return
        else:
            return
        token = token.replace('\n', '')
        yield token

tokens = oneToken()

first = int(next(tokens))
print (first)

last = int(next(tokens))
print (last)

all_available = [float(next(tokens)) for i in range(0, last)]
print (all_available)

data = all_available[first:last]
print (data)

输出:

3
11
[5.0, 6.2, 4.0, 6.0, 2.0, 5.0, 3.2, 6.0, 7.0, 1.4, 5.0]
[6.0, 2.0, 5.0, 3.2, 6.0, 7.0, 1.4, 5.0]

【讨论】:

  • 有趣! (附带说明,如果您有first, last == 3, 11,则您拥有/需要阅读的样本数应该是 9。但它很容易更改)。所以你的方法是从头开始重新创建一个数字解析器。我有两个后续问题: 1. 为什么使用 shlex 而不是普通的正则表达式? 2. 你的方法几乎正是我想要的。但是,难道没有更直接的方法来使用(文件)流中的这些数字,而不是从头开始创建令牌消费者吗?
  • 1.几天前,我对与您类似的问题提供了三个答案。答案之一涉及shlex。因此,它靠近我的脑海。 2. 可能有。
  • 你能给个链接到那个其他线程吗?我检查了您的一些答案(干得好,顺便说一句!)但找不到与我相关的答案。对于第 2 点。我原以为一次使用一个标记的文件流会很简单,但显然,Python 的哲学有点不同。甚至内置的 input 函数也需要并读取 whole 行。感谢您的意见。我没想到这种任务的水平如此之低。这是重锤,要谨慎使用;)我们被所有好的库和内置函数宠坏了。
  • 对了,说到锤子和小心,wordchar应该包括-eE...
【解决方案3】:

f.read() 会将剩余的数字作为字符串提供给您。你必须split 他们和mapfloat

import numpy as np
f = open('data.txt', 'r')
first, last = map(int, f.readline().split())
arr = np.zeros(last - first + 1)

data = map(float, f.read().split())

【讨论】:

  • 嗨,我知道f.read() 会在一次大扫除中获得所有剩余的内容。我展示了一个正在运行的示例。但一般read 不会这样做,因为人们无法事先知道size 要在f.read(size) 中读取以下数字。更糟糕的是,数字后面可以有/还有其他内容。
【解决方案4】:

Python 可以快速处理字符串。所以你可以尝试用两个分隔符来解决这个阅读问题。将其缩减为一个分隔符,然后读取(Python 3.):

import numpy as np
from io import StringIO

data = np.loadtxt(StringIO(''.join(l.replace(' ', '\n') for l in open('tata.txt'))),delimiter=' ',skiprows=2)

https://docs.scipy.org/doc/numpy/reference/generated/numpy.loadtxt.html

数据类型默认为浮点数。

【讨论】:

  • loadtxt 将文件加载为 (3,4) 数组(跳过第一行)不是更简单。如果需要,它可以重新塑造。假设整个文件在所有行中都有一致的列数。
  • 文件结构不是很清晰,可能会改变。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-16
  • 2017-08-16
  • 2020-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多