没有 For 循环的 Monotonify(列表修改)
Mathematica 代码中的 For 或 While 循环总是让我感觉有点脏,但我很困惑自己试图做一些类似功能的列表,并求助于这个:(
(* # Given a list of {x,y} pairs, transform the data as follows: every time
# there's a decrease in y-value from one datapoint to the next, say {x1,Y}
# followed by {x2,y}, add Y to the value of every datapoint on or after x2. *)
monotonify[data_] := Module[{data0, i, offset = 0},
data0 = data;
For[i = 2, i <= Length[data], i++,
If[data[[i-1,2]] > data[[i,2]], offset += data[[i-1,2]]];
data0[[i]] += {0,offset}];
data0]
将 y 值视为里程表读数,有时里程表会得到意外重置 - 很明显,因为值减少了,里程表不应该这样做,因此我们通过将每次重置之前的最后一个已知值添加到所有未来值来转换读数。)
您将如何以良好的功能风格编写 monotonify ?
(事实上,我不认为上述 For 循环完美可能是一种轻微的强迫症。)
For or While loops in Mathematica code always make me feel a little dirty but I was confusing myself trying to do some list munging all functional-like, and resorted to this:
(* # Given a list of {x,y} pairs, transform the data as follows: every time
# there's a decrease in y-value from one datapoint to the next, say {x1,Y}
# followed by {x2,y}, add Y to the value of every datapoint on or after x2. *)
monotonify[data_] := Module[{data0, i, offset = 0},
data0 = data;
For[i = 2, i <= Length[data], i++,
If[data[[i-1,2]] > data[[i,2]], offset += data[[i-1,2]]];
data0[[i]] += {0,offset}];
data0]
(Think of the y-values as odometer readings where sometimes the odometer gets accidentally reset -- evident because the value decreases, which odometers shouldn't do. So we transform the readings by adding the last known value before each reset to all future values.)
How would you write monotonify in a nice functional style?
(The fact that I don't consider the above For loop perfectly fine is probably a mild form of OCD.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
好的,现在我已经修复了按照最初要求处理输入的方法。
从示例数据集开始:
采用转置:
接下来是一个仅对 Y 值进行操作的函数:
撤消转置:
现在的输出是
我还没有测试此解决方案的性能。
编辑:好的,这是修复的基础,我将剩下的工作留给你@dreeves。此版本的 monotonify 仅适用于数字列表,我尚未将其集成到我之前的建议中以处理您的输入。
编辑2:另一个作用于数字列表的函数。这比我之前的尝试要快得多。
OK, now I've fixed my approach to work with inputs as originally requested.
Start with a sample dataset:
Take the transpose:
next a function to operate on the Y-values only:
Undo the transposition:
and the output is now
I still haven't tested the performance of this solution.
EDIT: OK, here's the basis of a fix, I'll leave the rest of the work to you @dreeves. This version of monotonify only works on a list of numbers, I haven't integrated it into my previous suggestion to work with your inputs.
EDIT 2: Another function which works on a list of numbers. This is much faster than my previous attempt.
这是另一个解决方案:
它计算一个校正向量,然后将其添加到给定数据点的 y 值。
Here is another solution:
It computes a correction vector that is then added to the y-values of the given data points.
一旦挑战结束,我就不能不尝试它,但我认为 For 循环版本更简单:
这个想法是编写一个辅助函数,它只针对 y 值的简单列表执行此操作,然后使用double-Transpose 习惯用法仅对数据的第二列进行操作。
双转置习惯用法的便捷参考
用于转换矩阵中的特定列,例如,将 4 列矩阵的第 2 列中的每个值 x 替换为 TransformElement[x]:
如果需要使用以下函数转换列 :将整个列作为列表,使用以下习惯用法:
Once the gauntlet was down I couldn't not try it, but I kind of think the For loop version is more straightforward:
The idea is to write a helper function that does it for just a plain list of the y-values and then use the double-Transpose idiom to operate on just the second column of the data.
Handy reference for the double-Transpose idiom
For transforming a particular column in a matrix, eg, replacing each value x in column 2 of a 4-column matrix with transformElement[x]:
If you need to transform a column with a function that takes the whole column as a list, use the following idiom:
我主要使用
Split
、Flatten
和Accumulate
来完成此操作。我不确定最终结果比 For 循环更容易理解,但如果重要的话,它应该是好的和快速的。我认为
With
的大量使用使得它比更多依赖匿名函数的解决方案更清晰一些。另外,monotonize
似乎是一种在“未修饰”列表上有用的东西,所以我将它分解为一个单独的函数。I did it using mostly
Split
,Flatten
andAccumulate
. I'm not sure the end result is easier to understand than theFor
loop, but it should be nice and fast if it matters.I think the copious use for
With
makes it a little clearer than a solution that relied more on anonymous functions woulld. Also,monotonize
seems like the kind of thing that could be useful on "undecorated" lists, so I broke it out as a separate function.从根本上来说,造成这一挑战的原因在于 Mathematica 中的大多数函数运算符一次只对列表中的一个元素进行操作。这不是唯一的选择,但是这些函数可以设置为一次获取列表中的两个相邻元素,这个假设的函数将使获得所需结果变得微不足道。
我们可以使用分区轻松地转换数据,而不是转换函数。
我重构了这个版本,添加了一个辅助函数,以明确折叠函数的工作原理,但 mathematica 也没有对其进行优化。
编辑:忘记了一些{}
Fundamentally what makes this challenging is that most functional operators in Mathematica operate on one element of a list at a time. This is not the only option however these functions could have been set up to take two adjacent elements of a list at a time, this hypothetical function would make it trivial to get the desired result.
Instead of transforming the function we can easily transform the data using Partition.
This version I refactored to add a helper function to make it clear how the function folded over works, but mathematica does not optimize it as well.
edit: forgot some {}