从txt文件创建一个数组

3
我是一名新手,在学习Python过程中遇到了问题。 我有一些测量数据保存在一个txt文件中。 数据用制表符分隔,具有以下结构:
0   0   -11.007001  -14.222319  2.336769

我每次模拟都有32个数据点(0,1,2,...,31),并且我有300个模拟(0,1,2,...,299),因此首先按照模拟编号和数据点编号对数据进行排序。
第一列是模拟编号,第二列是数据点编号,其他三列是x、y、z坐标。
我想创建一个三维数组,第一维应该是模拟编号,第二维是数据点编号,第三维是三个坐标。
我已经开始了一些工作,以下是我的进展:
## read file
coords = [x.split('\t') for x in
          open(f,'r').read().replace('\r','')[:-1].split('\n')]
## extract the information you want
simnum = [int(x[0]) for x in coords]
npts = [int(x[1]) for x in coords]
xyz = array([map(float,x[2:]) for x in coords])

但我不知道如何将这两个列表和一个数组组合起来。

最终我想要的是这样的:

array = [simnum][num_dat_point][xyz]

感谢您的帮助。

希望你能理解我的问题,这是我在Python论坛上的第一篇文章,如果我做错了什么,对此我很抱歉。

再次感谢。


3
这不仅仅是一个 Python 论坛,我添加了 Python 标签,以便于相关人士更容易地看到您的问题,并在顶部更改了格式,使得所有问题列表中的摘要更具信息性。 - Rich Seller
7个回答

2

您可以使用zip函数将它们组合在一起,如下所示:

for sim, datapoint, x, y, z in zip(simnum, npts, *xyz):
    # do your thing

或者你可以完全避免使用列表推导式,直接迭代文件的每一行:
for line in open(fname):
    lst = line.split('\t')
    sim, datapoint = int(lst[0]), int(lst[1])
    x, y, z = [float(i) for i in lst[2:]]
    # do your thing

要解析单行文本,你可以(也应该)执行以下操作:

coords = [x.split('\t') for x in open(fname)]

你说你的迭代示例“完全避免了”列表推导式,但是在倒数第二行中你使用了一个。 - JAB
你没有看到我使用lc和OPs之间的区别吗? - SilentGhost
@Cat:可以使用map(float, lst[2:]) - Roberto Bonvallet
2
@Roberto:确实。我只是想指出SilentGhost的代码在技术上仍然包含列表推导式,所以他的措辞并不完全正确。 - JAB
文件输入模块也可以在这种情况下使用。看看它是否更快会很有趣(我90%确定它会更快)。 - Vince

2
根据Python的编程哲学,扁平优于嵌套。我会使用字典。
import csv
f = csv.reader(open('thefile.csv'), delimiter='\t',
               quoting=csv.QUOTE_NONNUMERIC)

result = {}
for simn, dpoint, c1, c2, c3 in f:
    result[simn, dpoint] = c1, c2, c3

# pretty-prints the result:
from pprint import pprint
pprint(result)

你忘记将数据转换为适当的整数或浮点数。 - John Machin
不是固定的;可恶的 QUOTE_NONNUMERIC 把 所有 的 OP 数据隐式转换成浮点数。 - John Machin

2

这似乎是使用itertools.groupby的好机会。

import itertools
import csv
file = open("data.txt")
reader = csv.reader(file, delimiter='\t')
result = []
for simnumberStr, rows in itertools.groupby(reader, key=lambda t: t[0]):
    simData = []
    for row in rows:
        simData.append([float(v) for v in row[2:]])
    result.append(simData)
file.close()

这将创建一个名为“result”的三维列表。第一个索引是模拟编号,第二个索引是该模拟中的数据索引。值是包含x、y和z坐标的整数列表。

请注意,这假定数据已按模拟编号和数据编号排序。


格雷格 - 你使用的是哪个版本的Python,csv.reader函数需要一个分隔符参数?我的Python 2.6没有这个功能,这是在Python 3中添加的吗? - Petriborg
Petriborg--我正在使用Python 2.5.2版本。它也在这里有文档:http://docs.python.org/library/csv.html - Greg
1
@Petriborg,@Greg:如果您正确拼写分隔符,它会更好地工作。 - jcdyer

1

本质上,问题在于如果不同的模拟具有不同数量的点会发生什么。

因此,您需要首先将数组的维度调整为适当的大小。 它应该是至少max(simnum)x max(npts)x 3的数组。 为了消除混淆,您应该使用非数字进行初始化, 这将使您能够看到缺失的点。

然后使用类似以下的内容:

for x in coords:
  t[int(x[0])][int(x[1])][0]=float(x[3])
  t[int(x[0])][int(x[1])][1]=float(x[4])
  t[int(x[0])][int(x[1])][2]=float(x[5])

这是你想要的吗?


好的。那么这段代码应该可以与 t=[[[0,0,0] for i in range(32)] for j in range(300)] 一起正常工作。 - Sanjay Manohar

1
你可能会使用许多不同类型的容器来实现你的目的,但是没有一个容器的名称是array。Python有一个名为array的模块,你可以从标准库中导入它,但是array.array类型对于你的目的来说太过受限(只能是一维数组,且元素类型必须是基本类型);有一个流行的第三方扩展库称为numpy,它具有强大的numpy.array类型,如果你已经下载并安装了这个扩展库,你可以使用它--但是由于你甚至没有提到numpy,我怀疑这不是你的意思;相关的内置类型是listdict。我假设你想要任何类型的容器--但是如果你能在将来学会使用精确的术语,那将极大地帮助你和任何试图帮助你的人(当你指的是列表时,请说列表,当你确实指的是数组时,请仅使用数组,当你不确定要使用哪种容器时,请使用“容器”等)。

我建议您查看标准库中的csv模块,以更稳健的方式读取数据,但这是一个单独的问题。让我们从您拥有包含5个字符串的列表的coords列表开始,每个子列表都表示两个整数后跟三个浮点数的字符串。还需要指定另外两个关键方面...

您没有告诉我们的一个关键方面:列表是否按某种重要方式排序?特别地,您想保留某种重要顺序吗?由于您甚至没有提到任何一个问题,我将不得不假设一种或另一种方式,并且我将假设没有任何保证或有意义的顺序;但是,不允许重复(每个模拟/数据点号对只能出现一次)。

第二个关键问题是:每个模拟中的数据点数量是否按照递增顺序(0, 1, 2, ...)相同,还是这并不一定是这样的(而且顺便问一下,模拟本身是从0开始编号的吗?)再次强调,你没有提供关于规格的这个至关重要部分的线索 -- 注意到你只是“不告诉我们”关于如此显然关键的内容,导致可能帮助你的人们需要做出很多假设。不要让想要帮助你的人在黑暗中摸索:相反,学会以聪明的方式提问-- 这将为你自己和潜在的帮助者节省大量时间,并提供更高质量和更相关的帮助,那么为什么不这样做呢?无论如何,被迫再次做出假设,我将不得不假设关于模拟编号以及每个模拟中的数据点数量一无所知。

基于这些假设,dict 成为用于外部容器的唯一合理结构:一个字典,其键是一个包含两个项目的元组,模拟编号和模拟中数据点编号。值也可以是元组(每个元组包含三个浮点数),因为每行似乎确实有恰好3个坐标。

基于所有这些假设...:

def make_container(coords):
  result = dict()
  for s, d, x, y, z in coords:
    key = int(s), int(d)
    value = float(x), float(y), float(z)
    result[key] = value
  return result

将所有重要代码放在 def 语句中(即作为可调用函数,可能带有适当的参数)始终是最好且最快的做法,因此我以这种方式呈现它。 make_container 返回一个字典,您可以使用仿真编号和数据点编号进行访问;例如,

d = make_container(coords)
print d[0, 0]

将打印 sim 0 的 dp 0 的 x、y、z,假设这样的 sim/dp 组合存在(如果不存在这样的组合,则会出现错误)。字典有许多有用的方法,例如将上面的打印语句更改为

print d.get((0, 0))

(是的,在这里您确实需要双括号——内部括号用于创建元组,外部括号用于将该元组作为其唯一参数调用get),如果没有(0, 0)这样的sim/dp组合,您将看到None,而不是得到异常。

如果您可以编辑您的问题,使您的规格更加精确(可能包括一些指示您计划如何使用结果容器以及我列出的各种关键方面),我可能会能够更好地微调此建议以更好地匹配您的需求和情况(其他回答者也可能如此!),因此我强烈建议您这样做——提前感谢您帮助我们帮助您!-)


我每次模拟都有32个数据点(0,1,2,...,31),并且我有300次模拟(0,1,2...,299),因此数据首先按照模拟编号排序,然后按照数据点编号排序。希望这可以帮到你,感谢你的帮助,真的很有用。对于我的不太聪明的发布方式,我很抱歉,但我会尽力改进的。再次感谢。 - steffen
NP,我看到你已经接受了一个答案,这意味着那个答案已经解决了你的问题,我为你感到高兴! - Alex Martelli

0
首先,我想指出你的第一个数据点似乎是一个索引,不知道这个数据是否重要,但无论如何 :-)
def parse(line):
    mch = re.compile('^(\d+)\s+(\d+)\s+([-\d\.]+)\s+([-\d\.]+)\s+([-\d\.]+)$')
    m = mch.match(line)
    if m:
        l = m.groups()
        (idx,data,xyz) = (int(l[0]),int(l[1]), map(float, l[2:]))
        return (idx, data, xyz)
    return None

finaldata = []
file = open("data.txt",'r')
for line in file:
    r = parse(line)
    if r is not None:
        finaldata.append(r)

最终数据应该输出如下:

[(0, 0, [-11.007001000000001, -14.222319000000001, 2.3367689999999999]),
 (1, 0, [-11.007001000000001, -14.222319000000001, 2.3367689999999999]),
 (2, 0, [-11.007001000000001, -14.222319000000001, 2.3367689999999999]),
 (3, 0, [-11.007001000000001, -14.222319000000001, 2.3367689999999999]),
 (4, 0, [-11.007001000000001, -14.222319000000001, 2.3367689999999999])]

这应该相当健壮,可以处理空格问题(制表符、空格等)...

我也想知道你的数据文件有多大,我的通常很大,因此能够分块或分组处理它们变得更加重要... 无论如何,这将在Python 2.6中运行。


0
你确定你需要一个三维数组吗?更有可能的是,你需要一个二维数组,其中模拟编号是一个维度,数据点是第二个维度,然后存储在该位置的值是坐标。
这段代码将为您提供这个功能。
data = []
for coord in coords:
    if coord[0] not in data:
        data[coord[0]] = []
    data[coord[0]][coord[1]] = (coord[2], coord[3], coord[4])

要获取模拟7、数据点13的坐标,只需执行data[7][13]。

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