2009-09-01 2 views
3

Мне нужно сохранить большой список целых чисел в Bigtable (db). Для эффективности я храню их как разницу между двумя последовательными элементами.Эффективный способ использования lambda python, map

для например:

 original_list = [1005, 1004, 1003, 1004, 1006]

Хранение выше список (который на самом деле содержит более 1000K элементы), как

 
start = 1005 
diff = [-1, -1, 1, 2]

Ближайший я мог бы управлять это,

 
ltp = [start] 
map(lambda x: ltp.append(ltp[-1] + x), tick)

I Я ищу эффективный способ его преобразования в исходный список.

ответ

6

Следующие работает для меня:

orig = [start] 
for x in diff: 
    orig.append(orig[-1] + x) 

Использование map создаст новый массив того же размера, заполненный None. Я также нахожу простой цикл for более читаемым, и в этом случае так быстро, как вы можете получить.

+0

У меня это было и у меня в голове. Но он отказался от мысли, что карта и лямбда более эффективны, чем цикл. Может быть, я ошибаюсь :( – sudhakar

+3

@sudhakar: В Python лямбда очень неэффективна - вызовы функций медленны, а вызовы анонимных функций еще медленнее. –

+2

@fsanches: в некоторых случаях понимание списков происходит быстрее, но в этом случае (создание списка с использованием предыдущих значений) будет значительно медленнее, потому что для этого потребуются промежуточные списки. –

0

Хотя я не понимаю, почему это должно быть более эффективным, я уверен, что для цикла даст лучшую производительность:

l = [start] 
for i in diff: 
    l.append(l[-1] + i) 
+0

Его для приложения, связанного с фондовым рынком. Мне нужно хранить по меньшей мере 65 тыс. Единиц галочки по тиковым данным. После нескольких испытаний за последние 3 месяца я решил эту структуру данных, где я буду хранить их как массив python (не список) и сохраняться как Blob в appengines Bigtable. – sudhakar

+0

Перечисления списков, как правило, быстрее, чем для циклов. – fsanches

+0

Уточните, как вы построили бы это понимание списка? Я не могу придумать тот, который является изящным и не создает новый список каждый раз. – bayer

2

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

Если числа, которые хранятся, очень велики (то есть, переполняют целое число и требуют bignums), ваш список различий не принесет вам никакой эффективности - целое число является целым числом из POV-времени выполнения Python, поэтому ваш пример «diff» список [-1, -1, 1, 2] будет потреблять столько же памяти, сколько и исходный список [1005, 1004, 1003, 1004, 1006].

+0

Он отметил, что он хранит значения в БД. Я предположил, что он использует небольшой тип данных, например байт, в БД для хранения большого списка больших целых чисел, который * * более эффективен. – spatz

+0

Он использует тип 'array', предположительно с 8- или 16-битными значениями. –

+0

Да Я использую массив с 8-битным unsigned char tickvalue = ArrayProperty ('b', default = None) – sudhakar

7

Для таких больших структур данных numpy будет работать хорошо. Для этого примера, это над 200x быстрее (смотри ниже), и немного проще кода, в основном только

add.accumulate(diff) 

Сравнение между NumPy и прямого списка манипуляции:

import numpy as nx 
import timeit 

N = 10000 

diff_nx = nx.zeros(N, dtype=nx.int) 
diff_py = list(diff_nx) 

start = 1005 

def f0(): 
    orig = [start] 
    for x in diff_py: 
     orig.append(orig[-1] + x) 

def f1(): 
    diff_nx[0] = start 
    nx.add.accumulate(diff_nx) 

t = timeit.Timer("f0()", "from __main__ import f0, f1, diff_nx, diff_py, nx, start") 
print t.timeit(number=1000) 
t = timeit.Timer("f1()", "from __main__ import f0, f1, diff_nx, diff_py, nx, start") 
print t.timeit(number=1000) 

дает

13.4044158459  # for list looping 
0.0474112033844 # for numpy accumulate 

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

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

+0

+1 Я искал функцию накопления! –

+0

К сожалению, Appengine еще не поддерживает numpy :( – sudhakar

+1

Как последнее обновление, appengine поддерживает numpy сейчас https://developers.google.com/appengine/docs/python/tools/libraries27 – tacaswell

4

Идеально подходит для генераторов:

def diff2abs(diffs, start): 
    yield start 
    for diff in diffs: 
     start += diff 
     yield start 

start = 1005 
diffs = [-1, -1, 1, 2] 
original_list = list(diff2abs(diffs, start)) 
+0

+1 для использования генераторов – sudhakar

2
class runningtotal: 
    def __init__(self, start = 0): 
     self.total = start 
    def __call__(self, value): 
     self.total += value 
     return self.total 

Теперь попробуйте:

>>> map(runningtotal(start), [0,]+diff) 
[1005, 1004, 1003, 1004, 1006] 
+0

+1 для используя другой подход – sudhakar

1

Как mshsayem предложил, использовать списковых - они, как правило, быстрее, чем для петель или карты/лямбды (по ДЕЛАТЬ Книга Марка Лутца «Обучение Python»).

Если вы действительно хотите использовать более FP-ish-решение, правильная функция будет «проверять», что [я считаю] не реализовано на Python, поэтому вам придется реализовать его самостоятельно (что не является трудная задача).

«Сканирование» - это в основном сокращение, но вместо уменьшения списка до одного значения он сохраняет результат каждой «итерации» в новом списке.

Если вы реализовали его, вы могли бы сделать что-то вроде:

scan(lambda x,y: x+y, [start]++diff) 
+1

Вы правы, нет встроенного 'scan' в Python. –

+0

Мне очень нравится этот метод сканирования. Позвольте мне посмотреть, могу ли я придумать хорошую реализацию. +1 для разных подходов – sudhakar

+1

List comprehensions не всегда быстрее - особенно если (как в вашем примере) для каждой итерации создается новый список. В этом случае время выполнения увеличивается до n^2. – bayer

0

Я не знаю о ваших рассуждениях для хранения целых чисел в качестве файлов изменений - rcoder дал хороший ответ о том, почему это вообще не более эффективнее, чем хранить сами целые числа, но если вам не нужно иметь доступ ко всему списку сразу, для вас более эффективно использовать память для генератора. Поскольку вы говорите, что это «большой список», вы можете сэкономить много памяти таким образом, вместо того, чтобы распределять весь список сразу. Вот постижение генератор, чтобы получить список обратно:

start = 1005 
def mod_start(x): 
    global start 
    start += x 
    return start 
int_generator = (mod_start(i) for i in diffs) 

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

Вы можете очистить пример, чтобы переменная начала не была глобальной. Он просто не может быть локальным для функции mod_start.

Редактировать: Вам не обязательно использовать генератор для получения генератора. Вы также можете использовать функцию генератора с выражением yield, например THC4k. Это позволяет избежать проблемы с исходной переменной и, вероятно, немного чище. Вы также можете получить список из генератора в любой момент, передав его в встроенную функцию list().

0

Нет комментариев к исполнению этого, но здесь вы можете использовать сокращение.

start = 1005 
diffs = [-1,-1,1,2] 
reduce(lambda undiffed_list, diff: undiffed_list + [undiffed_list[-1] + diff],diffs,[start]) 

получает то, что вы хотите.

Смежные вопросы