数组条件循环的 Pythonic 代码(市场均值指数)

Pythonic code for conditional loop over array (Market Meaness Index)

我刚刚开始使用 Python,我以前的所有经验都是 C++ 类型的语言。

在尝试学习 "good" 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);
}

我很确定我可以使用 for 循环轻松地完成它,但我一直在尝试使用一系列数组操作而不是显式循环来完成它。我想到了:

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] ) 给出了一个 "truth value ambiguous" 错误,我不明白,但更重要的是我不确定这种方法是否会在 [=27= 中实际产生有效结果].

有人可以指导我如何优雅地编写代码,或者拍拍我的脑袋告诉我只使用循环吗?

伊恩

Python 是隐式的,不像 C++,在 C++ 中您几乎必须声明所有内容。 Python 和 numpy/pandas 或其他模块具有大量优化功能 built-in - 为了让您在没有大量循环或逐个值比较的情况下工作(模块在背景,虽然通常是一个 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

现在让我们解释一下:

系列基本上是一个数组,在这种情况下,系列似乎有点矫枉过正。如果将这样的对象与标量进行比较,您将得到一个布尔数组,表示哪个值满足条件,哪个不满足条件(比较两个数组也是如此,它将导致布尔数组通过值比较来表示值).

在第一步中,您将一个数组与一个标量进行比较(请记住,这将是一个布尔数组),并将另一个数组与另一个数组进行比较(我们将讨论移位部分),然后逻辑上和结合比较的结果。好消息是,如果您想组合两个布尔数组,这将通过 & 操作隐式工作。 第二步是类似的,并且将以相同的方式隐式工作。

在第三步中,你想要反转一个布尔数组并将其与另一个布尔数组组合。反转是由 ~ 运算符完成的,并且可以在很多其他地方使用(例如,用于反转子集选择等)。您不能在此处使用 not 运算符,因为它的目的是将其参数转换为真值 (True/False),而 return 相反 - 但数组的真值是多少?所有组件的逻辑和组合?它未定义,因此您会收到 ambiguous 错误。

布尔数组的 sum() 始终是数组中 True 个值的计数,因此它会产生正确的结果。

您的代码的唯一问题是,如果您将 shift(1) 应用于该系列,它会在前面添加一个 NaN 并截断该系列的最后一个元素,因此您最终会得到一个等长的物体。现在你的比较不再产生你想要的结果,因为任何与 numpy.NaN 相比的东西都会 return False。 为了克服这个问题,您可以简单地在开头定义第二个数组(这会使 pandas 过时),使用与之前 window 相同的语法。


PS:numpy 数组不是 python 列表(以上所有都是 numpy 数组!)numpy 数组是一个允许所有这些操作的复杂对象,具有标准 python 列表,你必须使用自己的 for 循环