Numpy字符串在Numpy字符串数组上调用tobytes()的奇怪行为



我正在尝试使用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')

最新更新