给定坐标对
data = {{1, 0}, {2, 0}, {3, 1}, {4, 2}, {5, 1},
{6, 2}, {7, 3}, {8, 4}, {9, 3}, {10, 2}}
我想提取山峰和山谷,因此:
{{4, 2}, {5, 1}, {8, 4}}
我目前的解决方案是这种笨拙:
Cases[
Partition[data, 3, 1],
{{ta_, a_}, {tb_, b_}, {tc_, c_}} /; Or[a < b > c, a > b < c] :> {tb, b}
]
您可以看到,使用Partition
将数据集的大小增加三倍。我认为可以使用Cases
和PatternSequence
来提取此信息,但此尝试不起作用:
Cases[
data,
({___, PatternSequence[{_, a_}, {t_, b_}, {_, c_}], ___}
/; Or[a < b > c, a > b < c]) :> {t, b}
]
产生{}
。
我认为模式没有任何问题,因为它适用于ReplaceAll
:
data /. ({___, PatternSequence[{_, a_}, {t_, b_}, {_, c_}], ___}
/; Or[a < b > c, a > b < c]) :> {t, b}
这给出了正确的第一个峰值{4, 2}
。这是怎么回事?
答案 0 :(得分:6)
失败尝试失败的原因之一是Cases
默认情况下会查找表达式第1级的匹配项。由于你在0级寻找匹配,你需要做类似
Cases[
data,
{___, {_, a_}, {t_, b_}, {_, c_}, ___} /; Or[a < b > c, a > b < c] :> {t, b},
{0}
]
但是,这仅返回{4,2}
作为解决方案,因此它仍然不是您正在寻找的。
要查找所有匹配而不进行分区,您可以执行类似
ReplaceList[data, ({___, {_, a_}, {t_, b_}, {_, c_}, ___} /;
Or[a < b > c, a > b < c]) :> {t, b}]
返回
{{4, 2}, {5, 1}, {8, 4}}
答案 1 :(得分:5)
你的“笨拙”解决方案相当快,因为它严重限制了所看到的内容。
这是一个例子。
m = 10^4;
n = 10^6;
ll = Transpose[{Range[n], RandomInteger[m, n]}];
In[266]:=
Timing[extrema =
Cases[Partition[ll, 3,
1], {{ta_, a_}, {tb_, b_}, {tc_, c_}} /;
Or[a < b > c, a > b < c] :> {tb, b}];][[1]]
Out[266]= 3.88
In[267]:= Length[extrema]
Out[267]= 666463
这似乎比使用替换规则更快。
更快的是创建差异产品的标志表。然后选择不在列表末端的与1的符号产品对应的条目。
In[268]:= Timing[ordinates = ll[[All, 2]];
signs =
Table[Sign[(ordinates[[j + 1]] -
ordinates[[j]])*(ordinates[[j - 1]] - ordinates[[j]])], {j, 2,
Length[ll] - 1}];
extrema2 = Pick[ll[[2 ;; -2]], signs, 1];][[1]]
Out[268]= 0.23
In[269]:= extrema2 === extrema
Out[269]= True
在这些方法中不考虑连续相等纵坐标的处理。这样做需要更多的工作,因为必须考虑大于三个连续元素的邻域。 (我的拼写检查员希望我在“邻居”的中间音节添加“你”。我的拼写检查员必须认为我们在加拿大。)
Daniel Lichtblau
答案 2 :(得分:2)
这可能不是你要求的实现,而是沿着这些方向:
ClearAll[localMaxPositions];
localMaxPositions[lst : {___?NumericQ}] :=
Part[#, All, 2] &@
ReplaceList[
MapIndexed[List, lst],
{___, {x_, _}, y : {t_, _} .., {z_, _}, ___} /; x < t && z < t :> y];
示例:
In[2]:= test = RandomInteger[{1,20},30]
Out[2]= {13,9,5,9,3,20,2,5,18,13,2,20,13,12,4,7,16,14,8,16,19,20,5,18,3,15,8,8,12,9}
In[3]:= localMaxPositions[test]
Out[3]= {{4},{6},{9},{12},{17},{22},{24},{26},{29}}
一旦你有职位,你可以提取元素:
In[4]:= Extract[test,%]
Out[4]= {9,20,18,20,16,20,18,15,12}
请注意,这也适用于一行中有多个相同最大元素的高原。要获得最小值,需要轻松更改代码。我实际上认为ReplaceList
是比Cases
更好的选择。
将其与您的数据一起使用:
In[7]:= Extract[data,localMaxPositions[data[[All,2]]]]
Out[7]= {{4,2},{8,4}}
和最小值相同。如果你想要结合起来,上述规则的变化也是微不足道的。
答案 3 :(得分:2)
另一种选择:
Part[#,Flatten[Position[Differences[Sign[Differences[#[[All, 2]]]]], 2|-2] + 1]] &@data
(* ==> {{4, 2}, {5, 1}, {8, 4}} *)
Extract[#, Position[Differences[Sign[Differences[#]]], {_, 2} | {_, -2}] + 1] &@data
(* ==> {{4, 2}, {5, 1}, {8, 4}} *)
答案 4 :(得分:1)
由于您对“笨拙”方法的一个主要担忧是使用分区进行的数据扩展,您可能需要了解Developer`
函数PartitionMap
,它不会对所有数据一下子。我使用Sequence[]
删除了我不想要的元素。
Developer`PartitionMap[
# /. {{{_, a_}, x : {_, b_}, {_, c_}} /; a < b > c || a > b < c :> x,
_ :> Sequence[]} &,
data, 3, 1
]