Looping through a Vector - arrays

I am using Rstudio and trying to create a function that will loop through a vector and perform a calculation with a while condition. The function should then return a data frame with the entered vector in one column and the number of iterations it took to satisfy the while condition in the another.
I have already created a function that preformed the calculation with the while condition which serves as the basic operation for the function I am having problems with. Here it is:
t5<-function(x){
z=x
while(x != 1){
if(x %% 2 == 0)
x= x/2
else x= (3 * x +1)
z=c(z, x)
}
return (z)
}
Here is what I have for the new function...my problem function (t7):
t7<-function(x){
y=0
i=0
for(i in 1:length(x)){
y[i]=length(t5(x[i]))-1
print(y[i])
}
#m<-data.frame(x, y[i])
}
I had it print y[i] because that is the only way the function does something. here is the output it shows (which is only half of what I need):
t7(2:10)
[1] 1
[1] 7
[1] 2
[1] 5
[1] 8
[1] 16
[1] 3
[1] 19
[1] 6
Can anybody help me understand how to make t7(2:10) run through this array and return a data frame listing the array and the number of iterations it took to reach the number 1 for each number in the array? Any help would be appreciated.

You can obtain the vector you need with the sapply function:
data.frame(x=2:10, iters=sapply(2:10, function(x) length(t5(x))-1))
# x iters
# 1 2 1
# 2 3 7
# 3 4 2
# 4 5 5
# 5 6 8
# 6 7 16
# 7 8 3
# 8 9 19
# 9 10 6

Related

get x elements from center of vector

How do I create a function (e.g. here, an anonymous one but I don't mind any) to get x elements from vec that are most centered (i.e. around the median)? In essence I want a function with same syntax as Matlab's randsample(n,k), but for non-random, with elements spanning around the center.
cntr=#(vec,x) vec(round(end*.5)+(-floor(x/2):floor(x/2))); %this function in question
cntr(1:10,3) % outputs 3 values around median 5.5 => [4 5 6];
cntr(1:11,5) % outputs => [4 5 6 7 8]
Note that vec is always sorted.
One part that I struggle with is not to output more than the limits of vec. For example, cntr(1:10, 10) should not throw an error.
edit: sorry to answer-ers for many updates of question
It's not a one-line anonymous function, but you can do this pretty simply with a couple calls to sort:
function vec = cntr(vec, x)
[~, index] = sort(abs(vec-median(vec)));
vec = vec(sort(index(1:min(x, end))));
end
The upside: it will still return the same set of values even if vec isn't sorted. Some examples:
>> cntr(1:10, 3)
ans =
4 5 6
>> cntr(1:11, 5)
ans =
4 5 6 7 8
>> cntr(1:10, 10) % No indexing errors
ans =
1 2 3 4 5 6 7 8 9 10
>> cntr([3 10 2 4 1 6 5 8 11 7 9], 5) % Unsorted version of example 2
ans =
4 6 5 8 7 % Same values, in their original order in vec
OLD ANSWER
NOTE: This applied to an earlier version of the question where a range of x values below and x values above the median were desired as output. Leaving it for posterity...
I broke it down into these steps (starting with a sorted vec):
Find the values in vec less than the median, get the last x indices of these, then take the first (smallest) of them. This is the starting index.
Find the values in vec greater than the median, get the first x indices of these, then take the last (largest) of them. This is the ending index.
Use the starting and ending indices to select the center portion of vec.
Here's the implementation of the above, using the functions find, min, and max:
cntr = #(vec, x) vec(min(find(vec < median(vec), x, 'last')):max(find(vec > median(vec), x)));
And a few tests:
>> cntr(1:10, 3) % 3 above and 3 below 5.5
ans =
3 4 5 6 7 8
>> cntr(1:11, 5) % 5 above and 5 below 6 (i.e. all of vec)
ans =
1 2 3 4 5 6 7 8 9 10 11
>> cntr(1:10, 10) % 10 above and 10 below 5.5 (i.e. all of vec, no indexing errors)
ans =
1 2 3 4 5 6 7 8 9 10
median requires sorting the array elements. Might as well sort manually, and pick out the middle block (edit: OP's comment indicates elements are already sorted, more justification for keeping it simple):
function data = cntr(data,x)
x = min(x,numel(data)); % don't pick more elements than exist
data = sort(data);
start = floor((numel(data)-x)/2) + 1;
data = data(start:start+x-1);
You could stick this into a single-line anonymous function with some tricks, but that just makes the code ugly. :)
Note that in the case of an uneven division (when we don't leave an even number of elements out), here we prioritize an element on the left. Here is what I mean:
0 0 0 0 0 0 0 0 0 0 0 => 11 elements, x=4
\_____/
picking these 4 values
This choice could be made more complex, for example shifting the interval left or right depending on which of those values is closest to the mean.
Given data (i.e. vec) is already sorted, the indexing operation can be kept to a single line:
cntr = #(data,x) data( floor((numel(data)-x)/2) + (1:x) );
The thing that is missing in that line is x = min(x,numel(data)), which we need to add twice becuase we can't change a variable in an anonymous function:
cntr = #(data,x) data( floor((numel(data)-min(x,numel(data)))/2) + (1:min(x,numel(data))) );
This we can simplify to:
cntr = #(data,x) data( floor(max(numel(data)-x,0)/2) + (1:min(x,numel(data))) );

Store vectors with different length as a list in R

For the code below, I want to store the (a*b) for each edge in a vector op1 and after that to store all vectors in a list opp1. According to the given data: I should have 5 vectors op1each one with a different length (for example: length(op1[1]=3) while the rest of vectors of length 2 like length(op1[2]=2)) and I will have one list opp1 contain the vectors of different lengths.
Storing the values in the vectors is OK but my problem is when storing these vectors, the resulted list contain the vectors but with the same length, i.e opp1list consist of all op1 vectors with length 3 so there is a strange value added from no where.
library(igraph)
graph<-matrix(c(4,3,4,1,4,2,3,2,3,1),ncol=2,byrow=TRUE)
g<-graph.data.frame(d = graph, directed = FALSE)
v1<-c()
v2<-c()
n1<-list()
n2<-list()
op1<-c()
opp1<-list()
for (edge in 1:length(E(g))){
v1[edge] <- ends(graph = g, es = edge)[1]
v2[edge] <- ends(graph = g, es = edge)[2]
n1[[edge]] <- names(neighbors(g, v1[edge], mode = "all"))
n2[[edge]] <- names(neighbors(g, v2[edge], mode = "all"))
for (neighbor in 1:length(n2[[edge]])){
a<-as.numeric(v2[edge])
b<-as.numeric(n2[[edge]][neighbor])
c<-(a*b)
op1[neighbor]<-c
}
opp1[[edge]]<-op1
}
The list opp1 is:
opp1
[[1]]
[1] 12 3 6
[[2]]
[1] 4 3 6
[[3]]
[1] 8 6 6
[[4]]
[1] 8 6 6
[[5]]
[1] 4 3 6
while it should be:
opp1
[[1]]
[1] 12 3 6
[[2]]
[1] 4 3
[[3]]
[1] 8 6
[[4]]
[1] 8 6
[[5]]
[1] 4 3
Is there any advice how to fix the code, also is there another way to do the same work in a different way especially to save time and memory when working on big data.
Thanks in advance.
You have to initialize op1 inside the first for loop in the way to have a clean op1 in each cycle, like this:
for (edge in 1:length(E(g))){
op1<-c()
v1[edge] <- ends(graph = g, es = edge)[1]
v2[edge] <- ends(graph = g, es = edge)[2]
n1[[edge]] <- names(neighbors(g, v1[edge], mode = "all"))
n2[[edge]] <- names(neighbors(g, v2[edge], mode = "all"))
for (neighbor in 1:length(n2[[edge]])){
a<-as.numeric(v2[edge])
b<-as.numeric(n2[[edge]][neighbor])
c<-(a*b)
op1[neighbor]<-c
}
opp1[[edge]]<-op1
}
Your output:
opp1
[[1]]
[1] 12 3 6
[[2]]
[1] 4 3
[[3]]
[1] 8 6
[[4]]
[1] 8 6
[[5]]
[1] 4 3
Without for loop is better and faster:
n<-length(E(g))
v1x<-unlist(lapply(lapply(c(1:n),ends,graph=g),"[[",1))
v2x<-unlist(lapply(lapply(c(1:n),ends,graph=g),"[[",2))
n1x<-lapply(lapply(v1x,neighbors,graph=g,mode="all"),names)
b<-Map(as.numeric,lapply(lapply(v2x,neighbors,graph=g,mode="all"),names))
a<-mapply(rep, as.numeric(v2x), lapply(n2x,length))
opp1<-Map('*',a,b)
opp1
[[1]]
[1] 12 3 6
[[2]]
[1] 4 3
[[3]]
[1] 8 6
[[4]]
[1] 8 6
[[5]]
[1] 4 3

Pairs of random numbers Matlab

I am trying to generate random numbers between 1 and 6 using Matlab's randperm and calling randperm = 6.
Each time this gives me a different array let's say for example:
x = randperm(6)
x = [3 2 4 1 5 6]
I was wondering if it was possible to create pairs of random numbers such that you end up with x like:
x = [3 4 1 2 5 6]
I need the vector to be arranged such that 1 and 2 are always next to each other, 3 and 4 next to each other and 5 and 6 next to each other. As I'm doing something in Psychtoolbox and this order is important.
Is it possible to have "blocks" of random order? I can't figure out how to do it.
Thanks
x=1:block:t ; %Numbers
req = bsxfun(#plus, x(randperm(t/block)),(0:block-1).'); %generating random blocks of #
%or req=x(randperm(t/block))+(0:block-1).' ; if you have MATLAB R2016b or later
req=req(:); %reshape
where,
t = total numbers
block = numbers in one block
%Sample run with t=12 and block=3
>> req.'
ans =
10 11 12 4 5 6 1 2 3 7 8 9
Edit:
If you also want the numbers within each block in random order, add the following 3 lines before the last line of above code:
[~, idx] = sort(rand(block,t/block)); %generating indices for shuffling
idx=bsxfun(#plus,idx,0:block:(t/block-1)*block); %shuffled linear indices
req=req(idx); %shuffled matrix
%Sample run with t=12 and block=3
req.'
ans =
9 8 7 2 3 1 12 10 11 5 6 4
I can see a simple 3 step process to get your desired output:
Produce 2*randperm(3)
Double up the values
Add randperm(2)-2 (randomly ordered pair of (-1,0)) to each pair.
In code:
x = randperm(3)
y = 2*x([1 1 2 2 3 3])
z = y + ([randperm(2),randperm(2),randperm(2)]-2)
with result
x = 3 1 2
y = 6 6 2 2 4 4
z = 6 5 2 1 3 4

periodic structure in matlab

I'm trying to create a script to solve my problem, but I got stuck in one place.
So I have imported .txt file with 4x1 sized matrix (simplified to give an example in my case it might be 1209x1 matrix) which contains some coordinate X. And it's look like this:
0
1
2
3
That's coordinates for one period, and I need to get one column for different number of periods N . Each period is the same and lenght=L
So you can do it manually by this script, for example for N=3 periods:
X=[X; X+L; X+2*L];
so for example if L=3
then i will get
0
1
2
3
3
4
5
6
6
7
8
9
it works well but it's not efficient in case if I need to work with number of periods let's say N=1000 or if I need to change their number quickly. Any solution to parameterize this operation so I can just put number for N and get X for N periods?
Thanks and Regards
I don't have MATLAB on this machine so I can't test, but the most straightforward implementation would be something like:
n = 1000;
L = 3;
nvalues = length(X); % Assuming X is your initial vector
newx = zeros(n*nvalues, 1); % Preallocate new array
for ii = 0:(n-1)
startidx = (nvalues*ii) + 1;
endidx = nvalues*(ii+1);
newx(startidx:endidx) = X + ii*L
end
You can use bsxfun to create X, X+L, X+2*L, ... and then reshape it to a vector
>> F=bsxfun(#plus, X, (0:(N-1))*L)
F =
0 3 6
1 4 7
2 5 8
3 6 9
>> X=F(:)
X =
0
1
2
3
3
4
5
6
6
7
8
9
or in a more concise form:
>> X=reshape(bsxfun(#plus, X, (0:(N-1))*L), [], 1)
X =
0
1
2
3
3
4
5
6
6
7
8
9

Splitting a vector/array in R

I would like to use R for performing an operation similar to merge sort. I would like to split my vector/array in to two parts. My input is present in a variable named inp.
> inp <- c(5,6,7,8,9,1,2,3,4)
> inplen <- length(inp)
> left <- inp[1:ceiling(inplen/2)]
> right <- inp[ceiling(inplen/2)+1:inplen]
> left
[1] 5 6 7 8 9
> right
[1] 1 2 3 4 NA NA NA NA NA
> length(left)
[1] 5
> length(right)
[1] 9
Here you can see that though I split the vector in two halves the size of the right half is larger than the size of the left half. Also there are some entries in the right half that have the value NA. I am not able to figure out as to why the second vector created (called right) is having these extra entries.
You are running in to a (well-known) problem caused by a higher operator precedence for ":" than for "+":
ceiling(inplen/2)+1:inplen
[1] 6 7 8 9 10 11 12 13 14
NAs are being returned because your index exceeded the length of the vector.
You're missing a bracket:
right = inp[(ceiling(inplen/2)+1):inplen]
To expand, suppose we have
1 + 1:3
does this mean 1+(1:3) or (1+1):3. R interprets this as 1+(1:3) so when you type 1+1:3 you get:
1 + c(1,2,3)
which becomes
c(2,3,4)
Another common gotcha is:
R> x = 1:5
R> x[2:length(x)-1]
[1] 1 2 3 4
Instead of selecting elements 2 to 4, we have selected elements 1 to 4 by mistake.
You can use split for this, with cut to create the breakpoints:
split(inp,cut(seq(inplen),breaks=c(0,ceiling(inplen/2),inplen),labels=c("left","right")))
$left
[1] 5 6 7 8 9
$right
[1] 1 2 3 4

Resources