Related
I have a code where we first need to generate n + 1 numbers in a range with a given step. However, I don't understand how and why it works:
a = 2;
b = 7;
h = (b-a)/n;
x[0] = a;
Array[x, n+1, 0];
For[i = 0, i < n + 1, i++, x[i] = a + h*i]
My questions are:
Are elements of x automatically generated when accessed? There's no mention of x before the line x[0] = a
Shouldn't index access be like x[[i]]?
What exactly does Array do here? It isn't assigned to anything which confuses me
Try Range[2,10,2] for a range of numbers from 2 to 10 in steps of 2, etc.
Beyond that there some faults in your code, or perhaps in your understanding of Mathematica ...
x[0] = a defines a function called x which, when presented with argument 0 returns a (or a's value since it is previously defined). Mathematica is particular about the bracketing characters used [ and ] enclose function argument lists. Since there is no other definition for the function x (at least not that we can see here) then it will return unevaluated for any argument other than 0.
And you are right, doubled square brackets, ie [[ and ]], are used to enclose index values. x[[2]] would indeed refer to the second element of a list called x. Note that Mathematica indexes from 1 so x[[0]] would produce an error if x existed and was a list.
The expression Array[x, n+1, 0] does return a value, but it is not assigned to any symbol so is lost. And the trailing ; on the line suppresses Mathematica's default behaviour to print the return value of any expression you execute.
Finally, on the issue of the use of For to make lists of values, refer to https://mathematica.stackexchange.com/questions/7924/alternatives-to-procedural-loops-and-iterating-over-lists-in-mathematica. And perhaps ask further Mathematica questions at that site, the real experts on the system are much more likely to be found there.
I suppose I might add ... if you are committed to using Array for some reason ask another question specifically about that. As you might (not) realise, I recommend not using that function to create a list of numbers.
From the docs, Array[f, n, r] generates a list using the index origin r.
On its own Array[x, n + 1, 0] just produces a list of x functions, e.g.
n = 4;
Array[x, n + 1, 0]
{x[0], x[1], x[2], x[3], x[4]}
If x is defined it is applied, e.g.
x[arg_] := arg^2
Array[x, 4 + 1, 0]
{0, 1, 4, 9, 16}
Alternatively, to use x as a function variable the Array can be set like so
Clear[x]
With[{z = Array[x, n + 1, 0]}, z = {m, n, o, p, q}]
{x[0], x[1], x[2], x[3], x[4]}
{m, n, o, p, q}
The OP's code sets function variables of x in the For loop, e.g.
Still with n = 4
a = 2;
b = 7;
h = (b - a)/n;
For[i = 0, i < n + 1, i++, x[i] = a + h*i]
which can be displayed by Array[x, n + 1, 0]
{2, 13/4, 9/2, 23/4, 7}
also x[0] == 2
True
The same could be accomplished thusly
Clear[x]
With[{z = Array[x, n + 1, 0]}, z = Table[a + h*i, {i, 0, 4}]]
{2, 13/4, 9/2, 23/4, 7}
Note also DownValues[x] shows the function definitions
{HoldPattern[x[0]] :> 2,
HoldPattern[x[1]] :> 13/4,
HoldPattern[x[2]] :> 9/2,
HoldPattern[x[3]] :> 23/4,
HoldPattern[x[4]] :> 7}
For example something like:
pre_hold_list = [-2,0,0,-1,0,0,0,3,0,0]
hold_condition = lambda x:x != 0
output = np.hold(pre_hold_list, hold_condition)
[-2,-2,-2,-1,-1,-1,-1,3,3,3] #result of output
Here the condition is that the current value is not zero the function will hold the value that this condition is met until the next value that meets this condition (i.e. it will hold -2 then -1 then 3).
Searching for np.hold() or np.step() does not give me anything on google.
Nevermind I coded a function that does this using the cumulative nature of cumsum and diff. If there's a way to improve this please let me know.
def holdtil(x, condition):
condition_index = np.where(condition)[0]
condition_value = np.take(x, condition_index)
condition_value_diff = np.diff(condition_value)
holdtil_diff = np.zeros(len(x))
holdtil_diff[condition_index[0]] = condition_value[0]
holdtil_diff[condition_index[1:]] = condition_value_diff
return np.cumsum(holdtil_diff)
EDIT:
I did a performance check between my solution and #Willem Van Onsem and mine has a very slight edge in time.
def hold_func():
start = time.time()
for i in range(1000):
x = np.random.randint(-5, 5, 1000)
hold(x, x != 0)
print(time.time() - start)
def holdtil_func():
start = time.time()
for i in range(1000):
x = np.random.randint(-5, 5, 1000)
holdtil(x, x != 0)
print(time.time() - start)
hold_func()
holdtil_func()
#0.055173397064208984
#0.045740604400634766
You could use a trick here by using cumsum(..) [numpy-doc], and diff() [numpy-doc]:
import numpy as np
def hold(iterable, condition):
cond = np.array(condition)
vals = np.array(iterable)
a = vals * cond
a[cond] = np.diff(np.hstack(((0,), a[cond])))
return a.cumsum()
The first parameter is an iterable that contains the elements, the second parameter condition is an iterable of the same length with booleans.
For example:
>>> a
array([-2, 0, 0, -1, 0, 0, 0, 3, 0, 0])
>>> hold(a, a != 0)
array([-2, -2, -2, -1, -1, -1, -1, 3, 3, 3])
>>> hold(a, a != 0)
array([-2, -2, -2, -1, -1, -1, -1, 3, 3, 3])
The function works as follows. Furst we make a copy of the two iterables (and convert these to numpy arrays, if that is not yet the case). You can omit that if these are numpy arrays.
Next we perform an elementwise multiplication, to make the values where the condition does not hold zero.
Next we calculate the difference between each item where the condition holds and the next one, and we set that to a. Finally we can use the cummulative sum of a, since the .diff() ensured that this will result in the correct repetitions.
This is a fun little problem, and I wanted to check with the experts here if there is a better functional/Mathematica way to approach solving it than what I did. I am not too happy with my solution since I use big IF THEN ELSE in it, but could not find a Mathematica command to use easily to do it (such as Select, Cases, Sow/Reap, Map.. etc...)
Here is the problem, given a list values (numbers or symbols), but for simplicity, lets assume a list of numbers for now. The list can contain zeros and the goal is replace the each zero with the element seen before it.
At the end, the list should contain no zeros in it.
Here is an example, given
a = {1, 0, 0, -1, 0, 0, 5, 0};
the result should be
a = {1, 1, 1, -1, -1, -1, 5, 5}
It should ofcourse be done in the most efficient way.
This is what I could come up with
Scan[(a[[#]] = If[a[[#]] == 0, a[[#-1]], a[[#]]]) &, Range[2, Length[a]]];
I wanted to see if I can use Sow/Reap on this, but did not know how.
question: can this be solved in a more functional/Mathematica way? The shorter the better ofcourse :)
update 1
Thanks everyone for the answer, all are very good to learn from. This is the result of speed test, on V 8.04, using windows 7, 4 GB Ram, intel 930 #2.8 Ghz:
I've tested the methods given for n from 100,000 to 4 million. The ReplaceRepeated method does not do well for large lists.
update 2
Removed earlier result that was shown above in update1 due to my error in copying one of the tests.
The updated results are below. Leonid method is the fastest. Congratulation Leonid. A very fast method.
The test program is the following:
(*version 2.0 *)
runTests[sizeOfList_?(IntegerQ[#] && Positive[#] &)] :=
Module[{tests, lst, result, nasser, daniel, heike, leonid, andrei,
sjoerd, i, names},
nasser[lst_List] := Module[{a = lst},
Scan[(a[[#]] = If[a[[#]] == 0, a[[# - 1]], a[[#]]]) &,
Range[2, Length[a]]]
];
daniel[lst_List] := Module[{replaceWithPrior},
replaceWithPrior[ll_, n_: 0] :=
Module[{prev}, Map[If[# == 0, prev, prev = #] &, ll]
];
replaceWithPrior[lst]
];
heike[lst_List] := Flatten[Accumulate /# Split[lst, (#2 == 0) &]];
andrei[lst_List] := Module[{x, y, z},
ReplaceRepeated[lst, {x___, y_, 0, z___} :> {x, y, y, z},
MaxIterations -> Infinity]
];
leonid[lst_List] :=
FoldList[If[#2 == 0, #1, #2] &, First##, Rest##] & #lst;
sjoerd[lst_List] :=
FixedPoint[(1 - Unitize[#]) RotateRight[#] + # &, lst];
lst = RandomChoice[Join[ConstantArray[0, 10], Range[-1, 5]],
sizeOfList];
tests = {nasser, daniel, heike, leonid, sjoerd};
names = {"Nasser","Daniel", "Heike", "Leonid", "Sjoerd"};
result = Table[0, {Length[tests]}, {2}];
Do[
result[[i, 1]] = names[[i]];
Block[{j, r = Table[0, {5}]},
Do[
r[[j]] = First#Timing[tests[[i]][lst]], {j, 1, 5}
];
result[[i, 2]] = Mean[r]
],
{i, 1, Length[tests]}
];
result
]
To run the tests for length 1000 the command is:
Grid[runTests[1000], Frame -> All]
Thanks everyone for the answers.
Much (order of magnitude) faster than other solutions still:
FoldList[If[#2 == 0, #1, #2] &, First##, Rest##] &
The speedup is due to Fold autocompiling. Will not be so dramatic for non-packed arrays. Benchmarks:
In[594]:=
a=b=c=RandomChoice[Join[ConstantArray[0,10],Range[-1,5]],150000];
(b=Flatten[Accumulate/#Split[b,(#2==0)&]]);//Timing
Scan[(a[[#]]=If[a[[#]]==0,a[[#-1]],a[[#]]])&,Range[2,Length[a]]]//Timing
(c=FoldList[If[#2==0,#1,#2]&,First##,Rest##]&#c);//Timing
SameQ[a,b,c]
Out[595]= {0.187,Null}
Out[596]= {0.625,Null}
Out[597]= {0.016,Null}
Out[598]= True
This seems to be a factor 4 faster on my machine:
a = Flatten[Accumulate /# Split[a, (#2 == 0) &]]
The timings I get are
a = b = RandomChoice[Join[ConstantArray[0, 10], Range[-1, 5]], 10000];
(b = Flatten[Accumulate /# Split[b, (#2 == 0) &]]); // Timing
Scan[(a[[#]] = If[a[[#]] == 0, a[[# - 1]], a[[#]]]) &,
Range[2, Length[a]]] // Timing
SameQ[a, b]
(* {0.015815, Null} *)
(* {0.061929, Null} *)
(* True *)
FixedPoint[(1 - Unitize[#]) RotateRight[#] + # &, d]
is about 10 and 2 times faster than Heike's solutions but slower than Leonid's.
You question looks exactly like a task for ReplaceRepeated function. What it does basically is that it applies the same set of rules to the expression until no more rules are applicable. In your case the expression is a list, and the rule is to replace 0 with its predecessor whenever occurs in a list. So here is the solution:
a = {1, 0, 0, -1, 0, 0, 5, 0};
a //. {x___, y_, 0, z___} -> {x, y, y, z};
The pattern for the rule here is the following:
x___ - any symbol, zero or more repetitions, the beginning of the list
y_ - exactly one element before zero
0 - zero itself, this element will be replaced with y later
z___ - any symbol, zero or more repetitions, the end of the list
While working on my answer to this question, it occurred to me that it is difficult to match a finite range of elements. With the built in patterns, you can match 1 element (_), 1 or more elements (__), or zero or more elements (___). To match more than one element, I used PatternSequence, like this
a:PatternSequence[_,_,_]
or, more generically
a:PatternSequence##Array[_&,3].
(Using a Condition would have also worked.) To match a range of n to m elements we could do
a:Alternatives##( PatternSequence ### Array[_&, {n,m}] ),
but that is a rather convoluted way to accomplish something that can be done by
a__ /; n <= Length[{a}] <= m.
However, this brings up an interesting question, using the Condition form it is straightforward to match the range 0 to n,
a___ /; Length[{a}] <= n,
but can this be done using patterns alone, i.e. without using Condition (/;)? More specifically, how would one go about matching 0 elements without adding a condition? Also, which is faster?
Maybe you could do something with Repeated. E.g.
Cases[{{1, 2, 3}, {1}, {1, 2, 3, 4, 5}, {1,2}}, {Repeated[_, {2, 4}]}]
gives the same result as
Cases[{{1, 2, 3}, {1}, {1, 2, 3, 4, 5}, {1,2}}, {a___ /; 2 <= Length[{a}] <= 4}]
The first method seems faster than the second. For example
tab = Table[Range[RandomInteger[1000]], {1000}];
Timing[t1 = Cases[tab, {a___ /; 0 <= Length[{a}] <= 100}];]
Timing[t2 = Cases[tab, {Repeated[_, {0, 100}]}];]
SameQ[t1, t2]
returns on my system
{0.027801, Null}
{0.000733, Null}
True
Summary: The basic question here was, I've discovered, whether you can pass a code block to a Ruby array which will actually reduce the contents of that array down to another array, not to a single value (the way inject does). The short answer is "no".
I'm accepting the answer that says this. Thanks to Squeegy for a great looping strategy to get streaks out of an array.
The Challenge: To reduce an array's elements without looping through it explicitly.
The Input: All integers from -10 to 10 (except 0) ordered randomly.
The Desired Output: An array representing streaks of positive or negative numbers. For instance, a -3 represents three consecutive negative numbers. A 2 represents two consecutive positive numbers.
Sample script:
original_array = (-10..10).to_a.sort{rand(3)-1}
original_array.reject!{|i| i == 0} # remove zero
streaks = (-1..1).to_a # this is a placeholder.
# The streaks array will contain the output.
# Your code goes here, hopefully without looping through the array
puts "Original Array:"
puts original_array.join(",")
puts "Streaks:"
puts streaks.join(",")
puts "Streaks Sum:"
puts streaks.inject{|sum,n| sum + n}
Sample outputs:
Original Array:
3,-4,-6,1,-10,-5,7,-8,9,-3,-7,8,10,4,2,5,-2,6,-1,-9
Streaks:
1,-2,1,-2,1,-1,1,-2,5,-1,1,-2
Streaks Sum:
0
Original Array:
-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8,9,10
Streaks:
-10,10
Streaks Sum:
0
Note a few things:
The streaks array has alternating positive and negative values.
The sum of the elements streaks array is always 0 (as is the sum of the original).
The sum of the absolute values of the streak array is always 20.
Hope that's clear!
Edit: I do realize that such constructs as reject! are actually looping through the array in the background. I'm not excluding looping because I'm a mean person. Just looking to learn about the language. If explicit iteration is necessary, that's fine.
Well, here's a one-line version, if that pleases you more:
streaks = original_array.inject([]) {|a,x| (a.empty? || x * a[-1] < 0 ? a << 0 : a)[-1] += x <=> 0; a}
And if even inject is too loopy for you, here's a really silly way:
streaks = eval "[#{original_array.join(",").gsub(/((\-\d+,?)+|(\d+,?)+)/) {($1[0..0] == "-" ? "-" : "") + $1.split(/,/).size.to_s + ","}}]"
But I think it's pretty clear that you're better off with something much more straightforward:
streaks = []
original_array.each do |x|
xsign = (x <=> 0)
if streaks.empty? || x * streaks[-1] < 0
streaks << xsign
else
streaks[-1] += xsign
end
end
In addition to being much easier to understand and maintain, the "loop" version runs in about two-thirds the time of the inject version, and about a sixth of the time of the eval/regexp one.
PS: Here's one more potentially interesting version:
a = [[]]
original_array.each do |x|
a << [] if x * (a[-1][-1] || 0) < 0
a[-1] << x
end
streaks = a.map {|aa| (aa.first <=> 0) * aa.size}
This uses two passes, first building an array of streak arrays, then converting the array of arrays to an array of signed sizes. In Ruby 1.8.5, this is actually slightly faster than the inject version above (though in Ruby 1.9 it's a little slower), but the boring loop is still the fastest.
new_array = original_array.dup
<Squeegy's answer, using new_array>
Ta da! No looping through the original array. Although inside dup it's a MEMCPY, which I suppose might be considered a loop at the assembler level?
http://www.ruby-doc.org/doxygen/1.8.4/array_8c-source.html
EDIT: ;)
original_array.each do |num|
if streaks.size == 0
streaks << num
else
if !((streaks[-1] > 0) ^ (num > 0))
streaks[-1] += 1
else
streaks << (num > 0 ? 1 : -1)
end
end
end
The magic here is the ^ xor operator.
true ^ false #=> true
true ^ true #=> false
false ^ false #=> false
So if the last number in the array is on the same side of zero as the number being processed, then add it to the streak, otherwise add it to the streaks array to start a new streak. Note that sine true ^ true returns false we have to negate the whole expression.
Since Ruby 1.9 there's a much simpler way to solve this problem:
original_array.chunk{|x| x <=> 0 }.map{|a,b| a * b.size }
Enumerable.chunk will group all consecutive elements of an array together by the output of a block:
>> original_array.chunk{|x| x <=> 0 }
=> [[1, [3]], [-1, [-4, -6]], [1, [1]], [-1, [-10, -5]], [1, [7]], [-1, [-8]], [1, [9]], [-1, [-3, -7]], [1, [8, 10, 4, 2, 5]], [-1, [-2]], [1, [6]], [-1, [-1, -9]]]
This is almost exactly what OP asks for, except the resulting groups need to be counted up to get the final streaks array.
More string abuse, a la Glenn McDonald, only different:
runs = original_array.map do |e|
if e < 0
'-'
else
'+'
end
end.join.scan(/-+|\++/).map do |t|
"#{t[0..0]}#{t.length}".to_i
end
p original_array
p runs
# => [2, 6, -4, 9, -8, -3, 1, 10, 5, -7, -1, 8, 7, -2, 4, 3, -5, -9, -10, -6]
# => [2, -1, 1, -2, 3, -2, 2, -1, 2, -4]