我正在尝试使用Numpy来向量化一个操作,以解析包含数行的文本文件,并将数据转换为Numpy数组。文本文件中的数据如下所示:
*** .txt file ***
1 0 0 0 0
2 1 0 0 0
3 1 1 0 0
4 0 1 0 0
5 0 0 1 0
6 1 0 1 0
7 1 1 1 0
8 0 1 1 0
9 0.5 0.5 0 0
10 0.5 0.5 1 0
11 0.5 0 0.5 0
12 1 0.5 0.5 0
13 0.5 1 0.5 0
14 0 0.5 0.5 0
*** /.txt file ***
我的方法是使用file.readlines()
读取中的行,然后将该行字符串列表转换为numpy数组,如下所示-file.readlines()
部分被省略以进行测试。
short_list = ['1 0 0 0 0n',
'2 1 0 0 0n',
'3 1 1 0 0n']
long_list = ['1 0 0 0 0n',
'2 1 0 0 0n',
'3 1 1 0 0n',
'4 0 1 0 0n',
'5 0 0 1 0n',
'6 1 0 1 0n',
'7 1 1 1 0n',
'8 0 1 1 0n',
'9 0.5 0.5 0 0n',
'10 0.5 0.5 1 0n',
'11 0.5 0 0.5 0n',
'12 1 0.5 0.5 0n',
'13 0.5 1 0.5 0n',
'14 0 0.5 0.5 0n']
def lines_to_npy(lines):
n_lines = len(lines)
lines_array = np.array(lines).astype('S')
tmp = lines_array.tobytes().decode('ascii')
print(repr(tmp))
print(lines_array.dtype)
print(np.array(tmp.split(), dtype=np.int32).reshape(n_lines, -1))
lines_to_npy(short_list)
lines_to_npy(long_list)
用short_list
调用函数会产生以下输出:
'1 0 0 0 0n2 1 0 0 0n3 1 1 0 0n'
|S10
[[1 0 0 0 0]
[2 1 0 0 0]
[3 1 1 0 0]]
这是所需的结果(从周围的阅读中,我发现"|S10"意味着数组中的每个元素都是一个10个字符的字符串,其字节序无关紧要)。但是,使用长列表进行调用会在每个字符串的末尾插入几个空字符x00
,这使得解析更加困难。
'1 0 0 0 0nx00x00x00x00x002 1 0 0 0nx00x00x00x00x003 1 1 0 0nx00x00x00x00x004 0 1 0 0nx00x00x00x00x005 0 0 1 0nx00x00x00x00x006 1 0 1 0nx00x00x00x00x007 1 1 1 0nx00x00x00x00x008 0 1 1 0nx00x00x00x00x009 0.5 0.5 0 0nx0010 0.5 0.5 1 0n11 0.5 0 0.5 0n12 1 0.5 0.5 0n13 0.5 1 0.5 0n14 0 0.5 0.5 0n'
|S15
请注意,在将空字符加载到数组中时,我的函数中出现了一个错误,导致无法得到最终结果。我知道;廉价且肮脏";解决方案是去掉末尾的空字符。我也知道我也可以用熊猫来实现主要目标,但我想了解为什么会发生这种行为。
CCD_ 5被填充在每个串的末端以使得每个串的长度为15。这是有道理的,因为短数组的dtype
是|S10
,而每个字符串恰好有10个字符长。长数组包含14个字符串,dtype
为|S15
,并附加额外的x00
,使数组中每个项目的长度为15个字符。
我很困惑,因为字符串列表中的元素数量(3比14)与每个字符串的长度没有相关性,所以我不明白为什么在添加更多列表元素时,dtype会变为|S15
。
更新:我对如何有效地将数据从文本文件读入numpy数组进行了更多的研究。我需要一种快速的方法来完成这项工作,因为我正在读取大约10M行的文件。numpy.loadfromtxt()
和numpy.genfromtxt()
是候选解决方案,但它们非常慢,因为它们是在Python中实现的,基本上与手动循环file.readlines()
、剥离和拆分字符串(源代码)相同。我在自己的测试中注意到,使用numpy.loadtxt()的速度大约是前面提到的手动方法的两倍,这里也提到了这一点。
我发现使用pandas.from_csv().to_numpy()
,我能够获得~10x的加速,这是通过file.readlines()
循环的速度。请在此处查看此答案。希望这能帮助将来使用相同应用程序的任何人。
我正在尝试使用Numpy来向量化一个操作,以解析包含数行的文本文件,并将数据转换为Numpy数组。
矢量化与读取数据无关。例如,tmp.split()
仍然在普通Python字符串对象上调用普通Python函数,从而创建大量Python字符串对象,并在主Python字节码解释器循环中执行。周围再多的Numpy代码也不会改变这一点。
也就是说,无论如何,这里都不会有任何有意义的绩效提升任何读和解释(即解析)文件的半合理方法都将比从硬盘驱动器中获取内容快得多,甚至比从SSD中读取要快得多。
我的方法是使用file.readlines()读取中的行,然后将该行字符串列表转换为numpy数组,如下所示-为了测试,省略了file.readline()部分。
不要那样做。整个过程比必要的要复杂得多。继续阅读。
tmp=lines_array.tobytes().decode('scii')
这只是为您提供了文件的原始内容,您可以直接使用.read()
而不是.readlines()
获得这些内容。
从周围的阅读中我收集到"|S10";意味着数组中的每个元素都是一个10个字符的字符串,其字节序与无关
不完全;元素是每个10字节的阵列(在C意义上)。他们不是"强者";字符串";它们是原始数据,可能被解释为文本。
字符串'1 0 0 0 0n'
,当使用默认编码编码为字节时,使用10个字节。short_list
中的所有其他字符串也是如此。因此;10字节的阵列";是一个合适的dtype。
会在每个字符串的末尾插入几个空字符\x00,这会使解析变得更加困难。
它不插入";空字符";;它插入null字节(数值为0)。它这样做是因为存储'14 0 0.5 0.5 0n'
的编码表示需要15个字节,并且每个元素的大小必须相同。
请记住,文本中的符号0
被翻译成一个字节,该字节的数值不为零。它的数值为48。
同样:所有这些编码和重新编码步骤都没有用处——您可以通过.read()
使用文件中的原始数据——.readlines()
所能帮助您的只是确定文件中的行数。
但您既不想也不需要做任何。
您想要的逻辑直接构建在Numpy中。你应该通过使用搜索引擎自己发现这一点。
你可以直接让Numpy为你加载文件,你应该这样做:numpy.loadtxt('myfile.txt')
。