ipython / Question about the %timeit looping process - ipython

Here is my code in Google Colab :
myArray=[]
Then
%%timeit -n 2
myArray.append("1")
The resultat gives :
Which I don't really understand (I was expecting only two values for myArray)

Timeit has two arguments you can pass to tell it how many times the code should be run: number (-n) and repeat (-r).
repeat tells timeit how many samples it should take
number specifies the number of times to repeat the code for each sample
Now, the default repeat value is 5. So, when number is 2 and repeat is 5, 2*5=10, which is the number of times the code is actually run and also the number of elements that get appended to the list.
To fix this you should also specify the repeat argument with -r.
Edit
For every sample you take (-r), you also run the setup code you may have passed to timeit. On the other hand, the number (-n) tells timeit how many times it should run your code for every sample. Your code is executed n times only after the setup, which is done r times, once for every sample.
From the timeit documentation:
timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None)
This is the timeit function signature. As you can see, there is a setup parameter you can pass to specify any code that should be run prior to executing your code (which will be executed number times, equivalent to -n) in the sample.
Let's now compare it with the timeit.repeat function:
timeit.repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=5, number=1000000, globals=None)
As you can see, there is an extra parameter here: repeat. Note that the default value of repeat (equivalent to -r) is 5, and this is why you get 10 elements appended to your list in your example.
Why should you use both -r and -n?
It's better to specify both the number of runs per sample, as well as the samples to take for comparability reasons. Think of every sample as you executing your Python script: Python has to load the script, perform some initial setup, and only then does it run your code.
You can also think of the number (-n) as the number of iterations in a for loop: the setup has already been done prior to running your code.
Here's a simplified Python-like pseudocode representation of what's happening when you use the timeit module:
def timeit(your_code, repeat, number, setup):
for r in range(repeat):
perform_setup()
for n in range(number):
run(your_code)
Hope this helps, cheers.

timeit has loops and runs. You just specified the loops per run.
> In [80]: alist = []
In [81]: %%timeit
...: alist.append(1)
...:
...:
146 ns ± 12.6 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
In [82]: len(alist)
Out[82]: 81111111
In [83]: alist=[]
In [84]: %%timeit -n2 -r1
...: alist.append(1)
...:
...:
1.75 µs ± 0 ns per loop (mean ± std. dev. of 1 run, 2 loops each)
In [85]: len(alist)
Out[85]: 2
In [86]: alist=[]
In [87]: %%timeit -n2
...: alist.append(1)
...:
...:
993 ns ± 129 ns per loop (mean ± std. dev. of 7 runs, 2 loops each)
In [88]: len(alist)
Out[88]: 14
Generally I try to setup a timeit so I don't care what the "result" is, since I want the times, not some sort of accumulated list or array.
For a fresh list each run:
In [89]: %%timeit -n2 -r10 alist=[]
...: alist.append(1)
...:
...:
The slowest run took 4.75 times longer than the fastest. This could mean that an intermediate result is being cached.
1.21 µs ± 889 ns per loop (mean ± std. dev. of 10 runs, 2 loops each)

Related

How to precalculate expensive Expressions in Polars (in groupby-s and in general)?

I'm having a hard time dealing with the fact that in a groupby I cant efficiently catch a group sub-dataframe with an Expr, perform an expensive operation with it once and then return several different aggregations. I can sort of do it (see example), but my solution is unreadable and looks like Im dealing with an unnecessary overhead because of all those lists. Is there a proper or a completely different way to do it?
Take a look at this example:
import polars as pl
import numpy as np
df = pl.DataFrame(np.random.randint(0,10,size=(1000000, 3)))
expensive = pl.col('column_1').cumprod().ewm_std(span=10).alias('expensive')
%%timeit
(
df
.groupby('column_0')
.agg([
expensive.sum().alias('sum'),
expensive.median().alias('median'),
*[expensive.max().alias(f'max{x}') for x in range(10)],
])
)
417 ms ± 38.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%%timeit
(
df
.groupby('column_0')
.agg(expensive)
.select([
pl.col('expensive').arr.eval(pl.element().sum()).arr.first().alias('sum'),
pl.col('expensive').arr.eval(pl.element().median()).arr.first().alias('median'),
*[pl.col('expensive').arr.eval(pl.element().max()).arr.first().alias(f'max{x}') for x in range(10)]
])
)
95.5 ms ± 9.16 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
We can see that precomputing the expensive part is beneficial, but actually doing it involves this .arr.eval(pl.element().<aggfunc>()).arr.first() that really bothers me because of both readability and flexibility. Try as I might, I cant see a better solution.
I'm not sure whether the problem is just about groupbys, if your solution involves dealing with selects, please share that also.
Use explode instead of arr.eval like this:
%%timeit
df \
.groupby('column_0') \
.agg(expensive).explode('expensive').groupby('column_0').agg([
pl.col('expensive').sum().alias('sum'),
pl.col('expensive').median().alias('median'),
*[pl.col('expensive').max().alias(f'max{x}') for x in range(10)]
])
On my machine the run times were
Your first example: 320 ms ± 18.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Your second: 80.8 ms ± 1.01 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Mine: 63 ms ± 507 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Another method which turns out to be slightly slower than the above is to do the expensive expression has a window function which then skips the explode
%%timeit
df.select(['column_0',expensive.over('column_0')]).groupby('column_0').agg([
pl.col('expensive').sum().alias('sum'),
pl.col('expensive').median().alias('median'),
*[pl.col('expensive').max().alias(f'max{x}') for x in range(10)]
])
This last one returned in 69.7 ms ± 911 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Efficiently implementing Matlab's "Find" function in Julia

I'm trying to implement Matlab's Find function in Julia. In Matlab, the code is
find(A==0)
where A is a very, very large n by m matrix, and where I iterate and update the above over a series of about 500 steps. In Julia, I implement the above via
[findall(x->x==0, D_tot)[j][2] for j in 1:count(x->x==0,D_tot)]
This seems to work nicely, except it goes very slow as I progress with my iteration. For example, for the first step, #time yields
0.000432 seconds (33 allocations: 3.141 KiB)
Step 25:
0.546958 seconds (40.37 k allocations: 389.997 MiB, 7.40% gc time)
Step 65:
1.765892 seconds (86.73 k allocations: 1.516 GiB, 9.63% gc time)
At each step, A remains the same size but becomes more complex, and Julia seems to have trouble finding the zeroes. Is there a better way of implementing Matlab's "Find" function than what I did above?
Going through the Matlab documentation I understand that you want to find
"a vector containing the linear indices of each nonzero element in array X"
and by non-zero you meant true values in Matlab's expression A==0
In that case this can be accomplished as
findall(==(0),vec(D_tot))
And a small benchmark:
D_tot=rand(0:100,1000,1000)
using BenchmarkTools
Running:
julia> #btime findall(==(0), vec($D_tot));
615.100 μs (17 allocations: 256.80 KiB)
julia> #btime findall(iszero, vec($D_tot));
665.799 μs (17 allocations: 256.80 KiB)

Parallel Looping with nested recursive for loop in MATLAB

Parallel Looping.
Hi there, I am having trouble figuring out what kind of loop I should be using for a section of my code.
my issue it that I would like to run the program with multiple initial conditions.for example lets say I have a matrix q where its dimensions are 4 by 2. q(:,1) is the first set of initial conditions and q(:,2) is the second set of initial conditions.
what I want is the recursive for loop (lines 11-22) to take the independent initial conditions q(:,1) and q(:,2) and simultaneously run them through the for loop independently.
Here is some mock code (unfortunately I cannot use the actual code due to an NDA):
1 p=ones(3,2); %initial condition
2 q=2.*ones(4,2); %initial condition
3 r=3.*ones(3,2); %initial condition
4 k=1; %here i want it to iterate for k=1:2 in parallel (i think)
5 pp=p(:,k);
6 qq=q(:,k);
7 rr=r(:,k);
8 p=p(:,k);
9 q=q(:,k);
10 r=r(:,k); %for loop below requires vector form.
11 for t=0:.5:60
12 v=[p;q;r]; %initial conditions for ode45
13 tspan=[t,t+.25,t+.5];
14 [T,Y]=ode45('somefunc',tspan,v,...);
15 v=Y(3,:);
16 p=v(1:3);
17 q=v(4:7);
18 r=v(8:10);
%building up a matrix with all the data from the iterated/recursive ode45
19 pp=cat(2,pp,p);
20 qq=cat(2,qq,q);
21 rr=cat(2,rr,r);
22 end
The code above evaluates for only k=1 or k=2. it does not iterate k=1:2.
what I want is to end up with a multi-dimensional array for pp, qq and rr for the two (at some point n) possible initial conditions. ie pp(:,:,1) would be the matrix with the first initial conditions and pp(:,:,2) would be the second initial conditions. same goes for qq and rr.
This is why I think I need a parallel for loop instead? so that the initial conditions in lines 1 through 3 can be called again rather than the redefined values of my variables due to the for loop in lines 11-22.
I am also confused on how to satisfy parfor's requirement of independent iterations while having a nested for loop with dependent iterations within.
Please let me know if there is anything else you would like me to explain further or clarify.
Thank you for your time and help!

Generate prime numbers recursively, Matlab

I am trying to generate prime numbers recursively using the previous primes. 2 is a prime by default and each time I keep adding 1 to the next number and divide by the previous primes, eg 2+1=3 so i check that 2 does not divide 3 so 3 is prime so i store 2 and 3 so far, next would be 3+1=4, i would use my current prime list 2 and 3, and see that 2 divides 4 so it does not go into the list then i continue with 4+1 and so forth all the way up to n. The key thing is dividing by the previous primes each time and if you reach a prime the list is updated. Please check my program to see what i am doing wrong.
this is my program so far but I am just getting 3 and 962, i want it update the list and refer back to it each time for the loop so i can use mod(2+numba,primlist) each time:
n=960;
primlist=[];
for numba=2:n
if mod(2+1,2)~=0
primlist=2+1;
end
if mod(2+numba,primlist)~=0
primlist=[primlist;2+numba];
end
end
primlist
You are initializing your primlist again and again. Do not do that. I am making as less modifications to your code to make it run correctly. The logic is essentially correct. You just need to initialize primlist outside.
n=960;
primlist=2;
for numba=1:n %Changed 2 to 1
if mod(2+numba,primlist)~=0
primlist=[primlist;2+numba];
end
end
primlist

Finding 5 consecutive successes using Matlab?

I have a function which for 10 cycles finds the difference between individual sensor values and the average sensor value. The test will be done 100 times using this function. So every time cycle>10 I am forcing it to be zero so that in the 11th repetition it will restart counting from zero. Here is the code:
cycle=cycle +1;
if cycle>10
cycle=0;
end
for i=1: TotalnoOfGrids
for j=1: noOfNodes
if abs(char(Allquants{i}(j))-char(mostCommonLetters {i}))>0
if cycle>0
wrong{i}(j)=wrong{i}(j)+1;
else
wrong{i}(j)=0;
end
end
end
end
Now I need to know if the sensor performed 5 consecutive successes in the period of 10 cycles. How can I do that?
I thought of a loop but I read that it takes too much time.
Doing a search on the net I have found this SO question.
The problem is that the above function will be repeated for 100 cycles.I want for every 10 cycles see if there is consecutive successes so it is beeing done dynamically and I am not saving the success or failure status of the sensor for the cycles. So i do not have a vector containing 1 or 0 to use the function used in the above reference or as Jonas suggested
If a loop is the easiest thing, give it a try! Just because you've read it "takes too much time" doesn't mean it really makes a difference for your case! It is true that in Matlab it often makes sense to avoid loops; but in your case, 100*20*9 (if I understand you correctly) loop iterations doesn't seem so bad yet (depending on your speed requirement).
Edit (corrected answer)
I now understand from your comments that the code you show us is surrounded by a while or for loop which is being run ~100 times, and that Allquants and mostCommonLetters probably change inside that loop. In this case my previous answer didn't work for you, since it counted successes on different sensors; this should be better now.
If I read your code correctly, the condition abs(char(Allquants{i}(j))-char(mostCommonLetters {i}))>0 tells you that a result was "wrong"; consequently,
for i=1:TotalnoOfGrids
this_cycle_successes(i,:)=char(Allquants{i})==char(mostCommonLetters{i});
end
consecutive_successes=(consecutive_successes+1).*this_cycle_successes;
would calculate how many successes you had in a row. Note you need to initialize consecutive_successes before starting your cycle loop, e.g.
consecutive_successes = zeros(9,20);
After the 10 cycles, you can check which sensors had 5 successes like this:
has5successes = consecutive_successes>=5;
Note that this is a matrix operation, so now you will get 9*20 values, as you requested in your comment. This solution wouldn't require a loop over j.