Самый быстрый способ конвертировать '(-1,0)' в кортеж (-1, 0)? - PullRequest
1 голос
/ 25 октября 2009

У меня огромный набор строк, которые возвращаются из программы. Возвращаемый пример кортежа может выглядеть так:

('(-1,0)', '(1,0)', '(2,0)', '(3,0)', '(4,0)', '(5,0)', '(6,0)')

Я могу преобразовать эти строки в реальные кортежи (с целыми числами внутри), но я надеюсь, что кто-то знает хороший прием, чтобы ускорить это. Все, что я придумал, кажется, что я делаю это относительно "медленным" способом. И, как я уже упоминал, эти списки могут быть большими, так что быстрый способ будет высоко оценен!

Спасибо

изменить один Хорошо, кажется, что eval - более медленный способ сделать это. Но пока у меня есть 4 метода, спасибо за любые комментарии и предложения! :)

Кроме того, кто-то спросил о размере моих кортежей. Он будет варьироваться от нескольких до, мы надеемся, не более нескольких миллионов. Не "слишком" большой, но достаточно большой, чтобы скорость была важным фактором. Я здесь не для микрооптимизации, просто изучаю новые хитрые трюки, о которых я мог не знать. Например, eval () - это то, о чем я часто забываю, хотя в этом случае он, похоже, не очень хорошо работает.

редактировать два Я также хотел отметить, что формат строки не должен меняться. Поэтому нет необходимости проверять формат. Кроме того, это встроенный Python v2.6.2, поэтому все, что требует 2.6, подойдет. 3.0 с другой стороны, не так много;)

Выглядит отлично, ребята, еще раз спасибо за все вклады:)

редактировать 3 Еще одна заметка. Я заметил, что возвращал код, который не приводил к «кортежу», это нормально, и извините, если кто-то думал, что конечный результат «должен» быть кортежем. Что-то вроде формата отлично.

import timeit

test_tuple = ('(-1,0)', '(1,0)', '(2,0)', '(3,0)', '(4,0)', '(5,0)', '(6,0)', '(7,0)',)

def timeit_a():
    ''''''
    def convert_tup_strings(tup_string):
        first_int, last_int = tup_string[1:-1].split(',')
        return (int(first_int), int(last_int))

    return map(convert_tup_strings, test_tuple)

def timeit_a_1():
    ''''''
    def convert_tup_strings(tup_string):
        return map(int, tup_string[1:-1].split(','))

    return map(convert_tup_strings, test_tuple)

def timeit_b():
    converted = []

    for tup_string in test_tuple:
        first_int, last_int = tup_string[1:-1].split(',')
        converted.append((int(first_int), int(last_int)))

    return converted

def timeit_b_1():
    converted = []

    for tup_string in test_tuple:
        converted.append(map(int, tup_string[1:-1].split(',')))

    return converted

def timeit_c():
    ''''''
    return [eval(t) for t in test_tuple]

def timeit_d():
    ''''''
    return map(eval, test_tuple)

def timeit_e():
    ''''''
    return map(lambda a: tuple(map(int, a[1:-1].split(','))), test_tuple)

print 'Timeit timeit_a: %s' % timeit.timeit(timeit_a)
print 'Timeit timeit_a_1: %s' % timeit.timeit(timeit_a_1)
print 'Timeit timeit_b: %s' % timeit.timeit(timeit_b)
print 'Timeit timeit_b_1: %s' % timeit.timeit(timeit_b_1)
print 'Timeit timeit_c: %s' % timeit.timeit(timeit_c)
print 'Timeit timeit_d: %s' % timeit.timeit(timeit_d)
print 'Timeit timeit_e: %s' % timeit.timeit(timeit_e)

Результаты:

Timeit timeit_a: 15.8954099772
Timeit timeit_a_1: 18.5484214589
Timeit timeit_b: 15.3137666465
Timeit timeit_b_1: 17.8405181116
Timeit timeit_c: 91.9587832802
Timeit timeit_d: 89.8858157489
Timeit timeit_e: 20.1564312947

Ответы [ 8 ]

10 голосов
/ 25 октября 2009

Я не советую вам использовать eval вообще. Это медленно и небезопасно. Вы можете сделать это:

result = map(lambda a: tuple(map(int, a[1:-1].split(','))), s)

Цифры говорят сами за себя:

timeit.Timer("map(lambda a: tuple(map(int, a[1:-1].split(','))), s)", "s = ('(-1,0)', '(1,0)', '(2,0)', '(3,0)', '(4,0)', '(5,0)', '(6,0)')").timeit(100000)

1.8787779808044434

timeit.Timer("map(eval, s)", "s = ('(-1,0)', '(1,0)', '(2,0)', '(3,0)', '(4,0)', '(5,0)', '(6,0)')").timeit(100000)

11.571426868438721
3 голосов
/ 25 октября 2009
map(eval, tuples)

Это не будет учитывать случай, когда один из кортежей не является синтаксически правильным. Для этого я бы порекомендовал что-то вроде:

def do(tup):
    try: return eval(tup)
    except: return None

map(do, tuples)

Оба метода проверены на скорость:

>>> tuples = ["(1,0)"] * 1000000

>>> # map eval
>>> st = time.time(); parsed = map(eval, tuples); print "%.2f s" % (time.time() - st)
16.02 s

>>> # map do
>>> >>> st = time.time(); parsed = map(do, tuples); print "%.2f s" % (time.time() - st)
18.46 s

Для 1 000 000 кортежей это не плохо (но тоже не отлично ). Предположительно, накладные расходы связаны с разбором Python миллион раз с использованием eval. Однако это самый простой способ сделать то, что вам нужно.

Ответ, использующий понимание списка вместо map, примерно такой же медленный, как мой пример попытки / исключения (интересный сам по себе):

>>> st = time.time(); parsed = [eval(t) for t in tuples]; print "%.2f s" % (time.time() - st)
18.13 s

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

2 голосов
/ 25 октября 2009

Мой компьютер работает медленнее, чем у Нади, однако он работает быстрее

>>> timeit.Timer(
    "list((int(a),int(c)) for a,b,c in (x[1:-1].partition(',') for x in s))", 
    "s = ('(-1,0)', '(1,0)', '(2,0)', '(3,0)', '(4,0)', '(5,0)', '(6,0)')").timeit(100000)
3.2250211238861084

чем это

>>> timeit.Timer(
    "map(lambda a: tuple(map(int, a[1:-1].split(','))), s)", 
    "s = ('(-1,0)', '(1,0)', '(2,0)', '(3,0)', '(4,0)', '(5,0)', '(6,0)')").timeit(100000)
3.8979239463806152

Использование понимания списка еще быстрее

>>> timeit.Timer(
    "[(int(a),int(c)) for a,b,c in (x[1:-1].partition(',') for x in s)]", 
    "s = ('(-1,0)', '(1,0)', '(2,0)', '(3,0)', '(4,0)', '(5,0)', '(6,0)')").timeit(100000)
2.452484130859375
2 голосов
/ 25 октября 2009

Я бы сделал анализ строки, если вы знаете формат. Быстрее, чем eval ().

>>> tuples = ["(1,0)"] * 1000000
>>> import time
>>> st = time.time(); parsed = map(eval, tuples); print "%.2f s" % (time.time() - st)
32.71 s
>>> def parse(s) :
...   return s[1:-1].split(",")
...
>>> parse("(1,0)")
['1', '0']
>>> st = time.time(); parsed = map(parse, tuples); print "%.2f s" % (time.time() - st)
5.05 s

если вам нужны целые числа

>>> def parse(s) :
...   return map(int, s[1:-1].split(","))
...
>>> parse("(1,0)")
[1, 0]
>>> st = time.time(); parsed = map(parse, tuples); print "%.2f s" % (time.time() - st)
9.62 s
1 голос
/ 25 октября 2009

вы можете просто использовать yaml или json, чтобы разобрать их в кортежи для вас.

1 голос
/ 25 октября 2009

С помощью YAPPS вы можете довольно быстро запустить и запустить парсер.

1 голос
/ 25 октября 2009

Если вы уверены, что вход правильно сформирован:

tuples = ('(-1,0)', '(1,0)', '(2,0)', '(3,0)', '(4,0)', '(5,0)', '(6,0)')
result = [eval(t) for t in tuples]
0 голосов
/ 25 октября 2009
import ast

list_of_tuples = map(ast.literal_eval, tuple_of_strings)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...