2017-01-18 6 views
3

Я только начинаю с Python, все мои предыдущие опыты были языками языка C++.Pythonic код для условного цикла над массивом (индекс рыночного значения)

В попытке узнать «хороший» Python я пытался преобразовать эту C-подобную функцию в Python.

var MMI(var *Data,int Length) 
{ 
    var m = Median(Data,Length); 
    int i, nh=0, nl=0; 
    for(i=1; i<Length; i++) { 
    if(Data[i] > m && Data[i] > Data[i-1]) 
     nl++; 
    else if(Data[i] < m && Data[i] < Data[i-1]) 
     nh++; 
    } 
    return 100.*(nl+nh)/(Length-1); 
} 

Я уверен, что я могу сделать это легко с цикл, но я пытался сделать это с помощью ряда операций с массивами, а не явный цикл. Я придумал:

import numpy as np 
import pandas as pd 
from pandas import Series 

def MMI(buffer, mmi_length): 
    window = Series(buffer[:mmi_length]) 
    m  = window.median() 

    nh = np.logical_and([window > m], [window > window.shift(1)]) 
    nl = np.logical_and([window < m], [window < window.shift(1)]) 
    nl = np.logical_and([not nh], [nl]) 

    return 100 * (nh.sum() + nl.sum())/mmi_length 

Окончательный np.logical_and([not nh], [n]) дает «значение истинности неоднозначной» ошибка, которую я не понимаю, но что более важно, я не уверен, будет ли этот подход на самом деле дают достоверный результат в Python ,

Может ли кто-нибудь предоставить указатель на то, как я должен это кодировать, или похлопать меня по голове и сказать, что я просто использую цикл?

Ян

+0

Ian, учитывая, что ваш интерес проявляется как в ML & algo-trading, вы можете получить удовольствие от ** 'numpy' strided-tricks >>> http://www.scipy-lectures.org/ advanced/advanced_numpy/# indexing-scheme-strides ** для серий, которые в обеих моделях квантов и ML скоро вырастут до форм, которые не могут быть реализованы для обработки InRAM. Учитывая, что переупаковка огромных объемов данных в абстракцию панды не уступает выжившей реализации. Если вы сомневаетесь, сравните код на 1M, 10M, 100M строках данных, чтобы увидеть ослабление производительности (что, очевидно, может показаться прощающим при крошечных и небольших размерах матрицы ...). – user3666197

+0

Спасибо. Я посмотрю, хотя, честно говоря, я настолько новичок в этом пространстве, что еще не достиг точки, когда у меня есть что-то полезное для применения к большому набору данных! –

ответ

4

Python неявно, в отличие от C++, где вы почти должны объявить все. Python и numpy/pandas или другие модули имеют тонну оптимизированных функциональных возможностей - для того, чтобы вы могли работать без большого количества циклов или сравнения значений по значениям (что делают модули в фоновом режиме, часто это цикл for хотя - так что не думайте, что это обязательно быстрее, это просто красивое обложка).

Теперь давайте посмотрим на код

import numpy as np # no need for pandas here 


def MMI(buffer, mmi_length): 
    # we will need to define two arrays here, 
    # shift(n) does not do what you want 
    window = np.asarray(buffer[1:mmi_length]) 
    window_shifted = np.asarray(buffer[:mmi_length-1]) 
    m = np.median(window) 

    # instead using all these explicit functions simply do: 
    nh = (window > m) & (window > window_shifted) 
    nl = (window < m) & (window < window_shifted) 
    nl = ~nh & nl        # ~ inverts a lot of things, 
               # one of them are boolean arrays 

    # this does the right thing 
    return 100*(nh.sum()+nl.sum())/mmi_length 

Теперь давайте объясним:

A Series в основном массив, в этом контексте, серия, кажется, как массовое убийство. Если вы сравниваете такой объект со скаляром, вы получите массив логических выражений, выражающий, какое значение удовлетворяло условию, а какое - нет (то же самое касается сравнения двух массивов, это приведет к булевому массиву, выражающему сравнение значений по значению).

На первом этапе вы сравниваете массив со скаляром (помните, что это будет логический массив) и еще один массив для другого массива (мы перейдем к части сдвига), а затем хотим логически и объединить результат сравнений. Хорошо, что вы хотите объединить два булевых массива, это будет работать неявно с помощью операции &. Второй шаг аналогичен и будет работать неявно.

На третьем этапе вы хотите инвертировать булевский массив и объединить его с другим булевым массивом. Инверсия выполняется оператором ~ и может использоваться в других местах в других местах (например, для инвертирования подмножеств и т. Д.). Вы не можете использовать здесь оператор not, поскольку его целью является преобразование его аргумента в значение истины (True/False) и возврат к противоположному - но каково значение истинности массива? Логическая и комбинация всех компонентов? Он не определен, поэтому вы получаете ошибку ambiguous.

Логический массив sum() логического массива всегда имеет значение True значений в массиве, поэтому он даст правильные результаты.

Единственная проблема с вашим кодом заключается в том, что если вы примените shift(1) к этой серии, то он добавит NaN и обрезает последний элемент серии, так что вы получите объект равной длины.Теперь ваши сравнения не дают того, чего вы хотите больше, потому что что-то по сравнению с numpy.NaN вернет False. Чтобы преодолеть это, вы можете просто определить второй массив в начале (который затем делает устаревшие панды), используя тот же синтаксис, который вы уже использовали для window.


PS: а NumPy массив это не список питона (все вышеперечисленное Numpy массивы!) NumPy массив представляет собой сложный объект, который позволяет для всех этих операций, со стандартными списками питона, вы должны работать ваш собственный для петель

+0

Спасибо - это невероятно полезно и действительно продвинуло мое понимание. Не в последнюю очередь потому, что в моей жизни я не мог понять, что такое инвертный оператор в Python! В вашем последнем приведенном примере кода «m = window.median()» не работает, поскольку только у Серии есть метод median(), поэтому я заменил его на «m = np.median (window)» –