数组条件循环的 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 循环
我刚刚开始使用 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 循环