Как условно превратить файл в двумерный массив? - PullRequest
0 голосов
/ 03 мая 2018

У меня есть файл в формате

2,3: true
3,5: false
4,2: true

Как я могу превратить строки с true в двумерный массив с размером

[[2,3],[4,2]]

Я пытался numpy.genfromtxt, но как я могу применить условие и ограничить строки чтения первыми двумя наборами цифр?

Ответы [ 2 ]

0 голосов
/ 03 мая 2018

Другой подход, который обрабатывает как смешанные разделители, так и условия, заключается в передаче файла через функцию фильтра.

Определите генератор, который принимает файл или все, что повторяется в строках, и возвращает отфильтрованный набор строк:

def foo(f):
    for line in f:
        x,y = line.split(':')
        if y.strip()=='true':
            yield x

Использование текстового standin для файла:

In [55]: txt='''2,3: true
    ...: 3,5: false
    ...: 4,2: true
    ...: 2,3: true
    ...: 3,5: false
    ...: 4,2: true'''

Генератор возвращает строки как:

In [56]: list(foo(txt.splitlines()))
Out[56]: ['2,3', '4,2', '2,3', '4,2']

genfromtxt легко превращает такой канал в массив:

In [57]: np.genfromtxt(foo(txt.splitlines()),delimiter=',', dtype=int)
Out[57]: 
array([[2, 3],
       [4, 2],
       [2, 3],
       [4, 2]])

genfromtxt повторяет файл в Python, поэтому использование foo не должно сильно изменять его скорость.

pandas имеет хороший csv-ридер, но в более быстрой скомпилированной версии не так много наворотов, как в Python.


Или я мог бы полностью пропустить genfromtxt:

def foo1(f):
    for line in f:
        x,y = line.split(':')
        if y.strip()=='true':
            yield x.split(',')

In [63]: np.array(list(foo1(txt.splitlines())), dtype=int)
Out[63]: 
array([[2, 3],
       [4, 2],
       [2, 3],
       [4, 2]])

Формат файла после выбора строк достаточно прост, чтобы np.array мог его обработать и напрямую преобразовать строку в целое.

0 голосов
/ 03 мая 2018

Пара миллионов строк целых, вероятно, не слишком велика для одновременной загрузки в массив NumPy (в зависимости от доступной оперативной памяти вашего компьютера). Таким образом, вы можете сгенерировать нужный массив, сначала загрузив полный числовой массив, а затем загрузив логическую маску:

import numpy as np
data = np.genfromtxt('data', delimiter=',', usecols=[0,1], comments=':', dtype=int)
mask = np.genfromtxt('data', delimiter=' ', usecols=[1], dtype=str) == 'true'
result = data[mask]

выходы

array([[2, 3],
       [4, 2]])

Я использовал два вызова np.genfromtxt, чтобы обойти проблему файла данных с двумя разными разделителями (то есть запятыми и пробелами).

Хотя загрузка всего массива может показаться расточительной, это гораздо быстрее, чем построчный анализ файла (при условии, что у вас достаточно памяти для этого.)

Оказывается - благодаря hpaulj за то, что побудили меня проверить - простой for-loop намного быстрее:

Например, при такой настройке:

import numpy as np

def make_data(N=10**6):
    data = np.random.randint(10, size=(N, 2))
    mask = np.array(['true', 'false'])[np.random.randint(2, size=N)]
    with open('data', 'w') as f:
        for row, maski in zip(data, mask):
            f.write('{},{}: {}\n'.format(row[0], row[1], maski))

def using_genfromtxt():
    data = np.genfromtxt('data', delimiter=',', usecols=[0,1], comments=':', dtype=int)
    mask = np.genfromtxt('data', delimiter=' ', usecols=[1], dtype=str) == 'true'
    result = data[mask]
    return result

def using_readline():
    """
    https://stackoverflow.com/a/50144016/190597 (hpaulj)
    """
    def foo1(f):
        for line in f:
            x,y = line.split(':')
            if y.strip()=='true':
                yield x.split(',')
    with open('data', 'r') as f:
        return np.array(list(foo1(f)), dtype=int)

make_data()

мы можем использовать IPython для оценки скорости using_genfromtxt против using_readline:

In [152]: %timeit using_genfromtxt()
1 loop, best of 3: 8.8 s per loop

In [171]: %timeit using_readline()
1 loop, best of 3: 861 ms per loop

Итак, простой for-loop на самом деле в 10 раз быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...