【问题标题】:Python - working with uneven columns in rowsPython - 处理行中不均匀的列
【发布时间】:2015-11-26 11:34:39
【问题描述】:

我正在处理包含数千行的数据,但列不均匀,如下所示:

AB  12   43   54

DM  33   41   45   56   33   77  88

MO  88   55   66   32   34 

KL  10   90   87   47   23  48  56  12

首先,我想读取列表或数组中的数据,然后找出最长行的长度。
然后,我将向短行添加零以使它们与最长的行相等,这样我就可以将它们作为二维数组进行迭代。

我尝试了其他几个类似的问题,但无法解决问题。

我相信 Python 中有一种方法可以做到这一点。谁能帮帮我?

【问题讨论】:

  • 您希望如何实际使用数据(“二维数组”)?我怀疑异构二维数组不是解决这个问题的正确数据结构。如果您只需要最长的行,那么您根本不需要 numpy。
  • 我正在考虑在短行的末尾添加零以使它们与最长的行相等。然后,我可以轻松索引整个数据。
  • 你是列表的数据列表吗?还是外部文件?如果您需要列表中最长的列表,您可以使用列表理解并轻松找到它。
  • 它在一个文本文件中。我需要先读取文件,然后找出最长的行。我在上面编辑了我的问题。请看一下。

标签: python numpy genfromtxt


【解决方案1】:

我没有看到任何更简单的方法来计算最大行长度,但只通过一次并找到它。然后,我们在第二遍中构建二维数组。比如:

from __future__ import print_function
import numpy as np
from itertools import chain

data = '''AB 12 43 54
DM 33 41 45 56 33 77 88
MO 88 55 66 32 34
KL 10 90 87 47 23 48 56 12'''

max_row_len = max(len(line.split()) for line in data.splitlines())

def padded_lines():
    for uneven_line in data.splitlines():
        line = uneven_line.split()
        line += ['0']*(max_row_len - len(line))
        yield line

# I will get back to the line below shortly, it unnecessarily creates the array
# twice in memory:
array = np.array(list(chain.from_iterable(padded_lines())), np.dtype(object))

array.shape = (-1, max_row_len)

print(array)

打印出来:

[['AB' '12' '43' '54' '0' '0' '0' '0' '0']
 ['DM' '33' '41' '45' '56' '33' '77' '88' '0']
 ['MO' '88' '55' '66' '32' '34' '0' '0' '0']
 ['KL' '10' '90' '87' '47' '23' '48' '56' '12']]

上面的代码效率很低,因为它在内存中创建了两次数组。我会回到它;我想我可以解决这个问题。

但是,numpy 数组应该是同构的。您想将字符串(第一列)和整数(所有其他列)放在同一个二维数组中。 我仍然认为您在这里走错了路,应该重新考虑问题并选择另一种数据结构或以不同的方式组织您的数据。我无法帮助您,因为我不知道您想如何使用这些数据。

(我很快就会回到创建两次的数组问题。)


正如所承诺的,这是效率问题的解决方案。请注意,我担心的是内存消耗。

    def main():

        with open('/tmp/input.txt') as f:
            max_row_len = max(len(line.split()) for line in f)

        with open('/tmp/input.txt') as f:
            str_len_max = len(max(chain.from_iterable(line.split() for line in f), key=len))

        def padded_lines():
            with open('/tmp/input.txt') as f:
                for uneven_line in f:
                    line = uneven_line.split()
                    line += ['0']*(max_row_len - len(line))
                    yield line

        fmt = '|S%d' % str_len_max
        array = np.fromiter(chain.from_iterable(padded_lines()), np.dtype(fmt))

这段代码可以做得更好,但我会留给你。

内存消耗,使用memory_profiler 测量随机生成的输入文件,该文件有 1000000 行,行长均匀分布在 1 到 20 之间:

Line #    Mem usage    Increment   Line Contents
================================================
     5   23.727 MiB    0.000 MiB   @profile
     6                             def main():
     7                                 
     8   23.727 MiB    0.000 MiB       with open('/tmp/input.txt') as f:
     9   23.727 MiB    0.000 MiB           max_row_len = max(len(line.split()) for line in f)
    10                                     
    11   23.727 MiB    0.000 MiB       with open('/tmp/input.txt') as f:
    12   23.727 MiB    0.000 MiB           str_len_max = len(max(chain.from_iterable(line.split() for line in f), key=len))
    13                                 
    14   23.727 MiB    0.000 MiB       def padded_lines():
    15                                     with open('/tmp/input.txt') as f:
    16   62.000 MiB   38.273 MiB               for uneven_line in f:
    17                                             line = uneven_line.split()
    18                                             line += ['0']*(max_row_len - len(line))
    19                                             yield line
    20                                 
    21   23.727 MiB  -38.273 MiB       fmt = '|S%d' % str_len_max
    22                                 array = np.fromiter(chain.from_iterable(padded_lines()), np.dtype(fmt))
    23   62.004 MiB   38.277 MiB       array.shape = (-1, max_row_len)

使用代码 eumiro 的答案,并使用相同的输入文件:

Line #    Mem usage    Increment   Line Contents
================================================
     5   23.719 MiB    0.000 MiB   @profile
     6                             def main():
     7   23.719 MiB    0.000 MiB       with open('/tmp/input.txt') as f:
     8  638.207 MiB  614.488 MiB           arr = np.array(list(it.izip_longest(*[line.split() for line in f], fillvalue='0'))).T

比较内存消耗增量:我更新后的代码消耗的内存比 eumiro 少 16 倍(614.488/38.273 约为 16)。

至于速度:我更新后的代码为此输入运行了 3.321 秒,eumiro 的代码运行了 5.687 秒,也就是说,我的代码在我的机器上快 1.7 倍。 (您的里程可能会有所不同。)

如果效率是您最关心的问题(正如您的评论所建议的那样“嗨 eumiro,我想这更有效。” 然后更改接受的答案),那么恐怕您接受的越少有效的解决方案。

别误会,eumiro 的代码真的很简洁,我确实从中学到了很多。如果效率不是我最关心的问题,我也会选择 eumiro 的解决方案。 p>

【讨论】:

  • 嗨,阿里,您的代码运行良好。这就是我想要的。非常感谢。非常感谢!!!
  • @user30337 请检查我更新的答案。我的解决方案消耗的内存比当前接受的答案少 16 倍,速度快 1.7 倍。
  • 再次感谢您。我对速度知之甚少。不过,我很感谢你们俩的帮助。
【解决方案2】:

您可以使用itertools.izip_longest 为您查找最长的线路:

import itertools as it
import numpy as np

with open('filename.txt') as f:
    arr = np.array(list(it.izip_longest(*[line.split() for line in f], fillvalue='0'))).T

arr 现在是:

array([['a', '1', '2', '0'],
       ['b', '3', '4', '5'],
       ['c', '6', '0', '0']], 
      dtype='|S1')

【讨论】:

  • 嗨eumiro,我想这更有效。非常感谢。我赞成你的回答。
猜你喜欢
  • 2018-06-11
  • 1970-01-01
  • 1970-01-01
  • 2018-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多