LINUX.ORG.RU

Как быстрее превратить текст в таблицу?

 


1

1

Есть таблица в текстовом файле, отформатированная пробелами. (~1000 строк и ~1000 столбцов.) Нужно превратить её в 2-мерный список. Сделал при помощи split() и splitlines(). Возник вопрос: как быстрее применить split() к результату splitlines()?

Можно заменять каждую строку на получаемый из неё список.

Можно создать пустой список и добавлять новые строки в него методом append().

Можно создать новый список через списковое включение (или как правильно называть list comprehension).

Ниже подсказали 4-й способ через map и lambda.

Решил померить:

#!/usr/bin/python3
from sys import argv
import time

replace, append, combine = 0.0, 0.0, 0.0

cycles = 20

for fil in argv[1:]:
    ar = open( fil, 'rt' ).read()

    for lcv in range(cycles):
        lines = ar.splitlines()
        t0 = time.perf_counter()
        for line in range(len(lines)):
            lines[line] = lines[line].split();
        t1 = time.perf_counter()
        replace += t1-t0
    
        lines = ar.splitlines()
        t0 = time.perf_counter()
        lines2 = []
        for line in range(len(lines)):
            lines2.append( lines[line].split() );
        t1 = time.perf_counter()
        append += t1-t0
    
        #lines = ar.splitlines()
        t0 = time.perf_counter()
        lines2 = [ l.split() for l in lines ]
        t1 = time.perf_counter()
        combine += t1-t0

        #lines = ar.splitlines()
        t0 = time.perf_counter()
        lines2 = [*map(lambda x: x.split(), lines), ]
        t1 = time.perf_counter()
        lamb += t1-t0

    print( 'replace, append, combine, lambda :', 
        replace/cycles, append/cycles, combine/cycles, lamb/cycles,
        '100', append/replace*100, combine/replace*100, lamb/cycles*100 )

Результат неожиданный. При малом числе циклов 1-й способ на 5% медленнее 2-го и на 20% быстрее 3-го. При большом — 1-й на 10-15% быстрее и 2-го и 3-го. (4-й способ — между 1-м и 3-м, +10% на 1 цикле, +5% на 100.) Почему так происходит?

P.S. Вопрос не как радикально ускорить, а почему они так различаются и почему многократное повторение так меняет результат.

★★★

Последнее исправление: olegd (всего исправлений: 3)

Ответ на: комментарий от vvn_black

with open('test.txt') as f:

А как-то можно это переделать, чтобы не включать время чтения из файла?

Попробовал 1 цикл — на 5% медленнее list comprehension.

olegd ★★★
() автор топика
Ответ на: комментарий от olegd

А как-то можно это переделать, чтобы не включать время чтения из файла?

А зачем?

Кстати, вот так может быть побыстрее:

def split_line(x):
    return x.strip().split(' ')

with open('test.txt') as f:
    lines = [*map(split_line, f), ]

Я как-то тестил что-то подобное у меня результат в таком порядке получился (начиная с самого шустрого): map с функцией, list comprehension, map с лямбдой.

vvn_black ★★★★★
()
Ответ на: комментарий от vvn_black

На большом числе циклов — на 15% быстрее LC и на 5% быстрее замены. То есть быстрее всех. Но это могут быть фокусы кеша.

olegd ★★★
() автор топика
Ответ на: комментарий от vvn_black

А зачем?

Чтобы кеш не влиял.

split(' ')

Не split(' '), а split(). Если пробелов несколько, split(' ') порождает пустые элементы.

olegd ★★★
() автор топика
Ответ на: комментарий от olegd

Не split(' '), а split()

Конечно, я же формат исходного текста в деталях не понял.

vvn_black ★★★★★
()
Ответ на: комментарий от vvn_black

map(split_line, lines)

Спасибо, разобрался.

olegd ★★★
() автор топика
Ответ на: комментарий от vvn_black

Если избавиться от многократного чтения с диска, то при однократном запуске с map быстрее простого list comprehension, но медленнее всех остальных. Если запускать десятки раз, то начинает обгонять и append.

olegd ★★★
() автор топика
Ответ на: комментарий от Sahas

попробуй numpy

numpy.loadtxt падает, если в строках получается разное число столбцов.

Попробовал для однократного чтения:

#!/usr/bin/python3
from sys import argv
import time
import numpy as np

for fil in argv[1:]:

    t0 = time.perf_counter()
    ar = open( fil, 'rt' ).read()
    lines = ar.splitlines()
    t1 = time.perf_counter()
    print( 'ar.splitlines():', t1-t0 )

    t0 = time.perf_counter()
    ar = open( fil, 'rt' ).read()
    lines = ar.splitlines()
    for line in range(len(lines)):
        lines[line] = lines[line].split()
    #print( lines )
    t1 = time.perf_counter()
    print( 'replace:', t1-t0 )

    t0 = time.perf_counter()
    ar = open( fil, 'rt' ).read()
    lines = ar.splitlines()
    lines2 = []
    for line in range(len(lines)):
        lines2.append(lines[line].split())
    #print( lines2 )
    t1 = time.perf_counter()
    print( 'append:', t1-t0 )

    t0 = time.perf_counter()
    ar = open( fil, 'rt' ).read()
    lines = ar.splitlines()
    lines = [ l.split() for l in lines ]
    #print( lines )
    t1 = time.perf_counter()
    print( '[]:', t1-t0 )

    t0 = time.perf_counter()
    with open(fil) as f:
      lines = [*map(lambda x: x.split(), f), ]
    #print(lines)
    t1 = time.perf_counter()
    print( 'lambda:', t1-t0 )

    t0 = time.perf_counter()
    lines = np.loadtxt( fil, dtype=str, comments=None, delimiter=None, converters=None, 
      skiprows=0, usecols=None, unpack=False, ndmin=2, encoding='bytes' )
    t1 = time.perf_counter()
    #print( lines )
    print( 'numpy:', t1-t0 )

На файл, с которым другие работали 0,21-0,23 секунд, Numpy.loadtxt затратила 2,5 с.

olegd ★★★
() автор топика
Последнее исправление: olegd (всего исправлений: 2)
Ответ на: комментарий от anonymous

Csv.reader попробуйте.

В моём случае нормально справляется splitlines(). Судя по беглому чтению мануала, модуль csv только обеспечивает парсинг, что для меня — и так не проблема, а средств для объединения в массив не предоставляет.

olegd ★★★
() автор топика
import re
pat = re.compile(' +')
split = re.split

res = []
for line in open('file.txt'):
    res.append(split(pat, line))

попробуй, я не п

anonymous
()
[map(str.split, l) for l in open('file.txt')]

ох уж эти любители многобукофф...

Но с т.з. производительности это может оказаться хуже обычного цикла.

AntonI ★★★★
()
Ответ на: комментарий от AntonI

strip() лишний - split() его де-факто и так делает.

Именно.

olegd ★★★
() автор топика
Ответ на: комментарий от AntonI

с т.з. производительности это может оказаться хуже обычного цикла.

Да, немного хуже.

olegd ★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.