如何使用python高效地加载此类ASCII文件?
我有大型fortran生成的ASCII文件,格式如下:如何使用python高效地加载此类ASCII文件?,python,numpy,io,Python,Numpy,Io,我有大型fortran生成的ASCII文件,格式如下: x y z num_line index 1 float 2 float ... num_line float x2 y2 z2 num_line2 index2 1 float 2 float ... num_line2 float ... 块的数量可以达到数千,每个块中的行数可以达到数百 让我们举一个我得到的例子: 0.0 0.0 0.0 4 0 1 0.5 2 0.9 3 0.4 4 0.1 0.0 0.0 1.0 4 1 1
x y z num_line index
1 float
2 float
...
num_line float
x2 y2 z2 num_line2 index2
1 float
2 float
...
num_line2 float
...
块的数量可以达到数千,每个块中的行数可以达到数百
让我们举一个我得到的例子:
0.0 0.0 0.0 4 0
1 0.5
2 0.9
3 0.4
4 0.1
0.0 0.0 1.0 4 1
1 0.2
2 0.2
3 0.4
4 0.9
0.0 1.0 2.0 5 2
1 0.7
2 0.6
3 0.9
4 0.2
5 0.7
我想从中得到什么(作为numpy矩阵):
当然,我可以使用:
my_mat = []
with open("myfile", "r") as f_in:
niter = int(f_in.readline().split()[3])
while niter:
curr_vect = zeros(niter)
for i in xrange(niter):
curr_vect[i] = float(f_in.readline().split()[1])
my_mat.append(curr_vect)
line = f_in.readline()
if line is not None:
niter = int(line.split()[3])
else:
niter = False
my_mat = array(my_mat)
问题是,这并不是真正有效的,也太复杂了。我已经知道numpy的loadtxt
和genfromtxt
但是它们似乎不适用于那里
我在寻找更快、更可读的东西。有什么想法吗
编辑:
请原谅我,我的问题不完整,你们中的一些人因为我而失去了时间。以下是此类块的真实示例:
3.571428571429E-02 3.571428571429E-02-3.571428571429E-02 1 35
1 -0.493775207966779
2 0.370269037864060
3 0.382332033744703
4 0.382332033744703
5 0.575515346181205
6 0.575515346181216
7 0.575562530624028
8 0.639458035564442
9 0.948445367602052
10 0.948445367602052
11 0.975303238888803
12 1.20634795229899
13 1.21972845646758
14 1.21972845646759
15 1.52659950368213
16 2.07381346028515
17 2.07629743909555
18 2.07629743909555
19 2.15941179949552
20 2.15941179949552
21 2.30814240005132
22 2.30814240005133
23 2.31322868361483
24 2.53625115348660
25 2.55301153157825
26 2.55301153157826
27 2.97152031842301
28 2.98866790318661
29 2.98866790318662
30 3.24757159459268
31 3.27186643004142
32 3.27186643004143
33 3.37632477135298
34 3.37632477135299
35 3.55393884607834
如果确保每个块都有相同数量的行(即使填充了零),速度会快得多。这样,您就可以使用
loadtxt
重新格式化读取的数组。但考虑到这一限制,这里有一个可能更快一些的示例:
import numpy as np
data = np.loadtxt("myfile", usecols=(0, 1), unpack=True)
nx = np.sum(data[0] == 0)
ny = np.max(data[0])
my_mat = np.empty((nx, ny), dtype='d')
my_mat[:] = np.nan # if you really want to populate it with NaNs for missing
tr_ind = data[0, list(np.nonzero(np.diff(data[0]) < 0)[0]) + [-1]].astype('i')
buf = np.squeeze(data[1, np.nonzero(data[0])])
idx = 0
for i in range(nx):
my_mat[i, :tr_ind[i]] = buf[idx : idx + tr_ind[i]]
idx += tr_ind[i]
更新:正如TheodrosZelleke所指出的,当x2
(第一列)为非零时,上述解决方案将失败。我第一次没有注意到这一点。以下是解决此问题的更新:
# this will give a conversion warning because column number varies
blk_sizes = np.genfromtxt("myfile", invalid_raise=False, usecols=(-2,))
nx = blk_sizes.size
ny = np.max(blk_sizes)
data = np.loadtxt("myfile", usecols=(1,))
my_mat = np.empty((nx, ny), dtype='d')
my_mat[:] = np.nan
idx = 1
for i in range(nx):
my_mat[i, :blk_sizes[i]] = data[idx : idx + blk_sizes[i]]
idx += blk_sizes[i] + 1
(然后取my_mat.T
)
输出:
编辑:
至于性能,我用
np.genfromtxt
解决方案@TheodrosZelleke对它进行了测试,速度似乎快了五倍左右。您可以使用numpy.genfromtxt:
import numpy as np
from StringIO import StringIO
# your data from above as string
raw = '''0.0 0.0 0.0 4 0
1 0.5
...
5 0.7
'''
这是转换器:
def custom_converter(line):
token = line.split()
if len(token) == 2:
return float(token[1])
else:
return np.NaN
加载数据:
data = np.genfromtxt(StringIO(raw),
delimiter='\n',
converters={0: custom_converter})
print data
其中打印:
[ nan 0.5 0.9 0.4 0.1 nan 0.2 0.2 0.4 0.9 nan 0.7 0.6 0.9 0.2
0.7]
现在构建最终的数据结构:
delims, = np.where(np.isnan(data))
max_block = np.max(np.diff(delims))
nblocks = delims.size
final_data = np.empty([max_block, nblocks]) + np.NaN
delims = delims.tolist()
delims.append(data.size)
low = delims[0] + 1
for i, up in enumerate(delims[1:]):
final_data[0: up-low , i] = data[low:up]
low = up + 1
print final_data
哪张照片
[[ 0.5 0.2 0.7]
[ 0.9 0.2 0.6]
[ 0.4 0.4 0.9]
[ 0.1 0.9 0.2]
[ nan nan 0.7]]
我不认为有一种简单而干净的方法可以做到这一点,除了像您那样直接编写特定于该格式的阅读器的代码。我想你需要多次处理这个文件?然后,我建议您使用上面提到的方法读取此文件一次,并将其转换为python或numpy易于读取的文件结构。如果文件发生了变化,您可能需要考虑在FORTRAN中更改输出格式(如果您可以访问该代码)。是的,<代码> PyTabs< /Code >将很乐意这样做。问题是我有很多这样格式化的文件。。。我也有很多文件格式相似,但不完全相同。比如说,在我听说loadtxt之前,我已经为csv表编写了多年这种程序。。。这是一个耻辱。不,有些人可能已经尝试了一些与蒂亚戈相同的假设(尚未出版)。我还没有回答你。我会的。我对你解决这个问题的方式很感兴趣,我想在评论之前先做一点实验@盖尔,在你的真实例子中,你有两个数字(一个负数),没有空格分隔它们。这将是一个非常烦人的问题,你必须使用固定的间距来阅读这些列。你是绝对正确的。Fortran Powa!然后我应该用硬编码的列范围替换拆分。-1)抱歉,但是您的算法取决于这样一个事实,即要排除的行以
0开头。
。虽然OP没有明确说明情况并非如此,但人们可能会猜测这些线(包含x、y、z坐标)将具有其他x值。我声称,仅仅通过查看前两列是不可能提供解决方案的——您需要查看每行上的令牌计数,您是正确的。我现在没有时间,但是改变算法避免零并不难。您可以始终查看第一列的导数(即使在某些情况下需要一些开关),或者通过查看其他列来找到提取这些数字的方法。我的解决方案不是一个完整的答案,而是一个起点。它是一个没有起点的起点——这个问题需要一个答案,这个答案并不意味着对前两列的数值有任何假设——你不能处理任何类型的导数。。。您需要查看每行上的令牌计数。前两列中有足够的信息来确定它何时跳转到另一个块。不管你相信与否都没有关系。如果数据直接保存在HDF5中,速度会更快,但事实并非如此。我可以调整我的fortran程序版本,但我不能为其他版本做。顺便说一句,我给了你一个街区的真实例子。零比其他数字更容易找到。。。对不起。我必须同意Theodros所写的:依靠数据推断数据结构似乎不是一个好主意。我刚才添加的示例中的下一个x可以等于3.6E01。这将导致无法识别的错误。不过,谢谢你的回答:我没有考虑这个方法,我会记住的。
data = np.genfromtxt(StringIO(raw),
delimiter='\n',
converters={0: custom_converter})
print data
[ nan 0.5 0.9 0.4 0.1 nan 0.2 0.2 0.4 0.9 nan 0.7 0.6 0.9 0.2
0.7]
delims, = np.where(np.isnan(data))
max_block = np.max(np.diff(delims))
nblocks = delims.size
final_data = np.empty([max_block, nblocks]) + np.NaN
delims = delims.tolist()
delims.append(data.size)
low = delims[0] + 1
for i, up in enumerate(delims[1:]):
final_data[0: up-low , i] = data[low:up]
low = up + 1
print final_data
[[ 0.5 0.2 0.7]
[ 0.9 0.2 0.6]
[ 0.4 0.4 0.9]
[ 0.1 0.9 0.2]
[ nan nan 0.7]]