Suppose I have a square of size10x10, then I divide this square in equal parts, for example, in 4 equal parts (could be other number like 2, 8, 16, ...).
After that, inside a loop I want to choose one of the 4 parts randomly and generate one point in this square. Here I will choose the second square.
min.x = 0
max.x=10
min.y=0
max.y=10
xd = xMax-xMin
yd = yMax-yMin
#generating randomly coordinates at the second square
set.seed(1)
xx_1 = 5*runif(1) + 5; yy_1 = 5*runif(1) + 0
#ploting the big square and the point in the second square just to ilustrate
For this example, if I'll do manually, I could use the following function for each one of the 4 squares:
xx_1 = 5*runif(1)+0; yy_1 = 5*runif(1)+0
xx_2 = 5*runif(1)+5; yy_2 = 5*runif(1)+0
xx_3 = 5*runif(1)+0; yy_3 = 5*runif(1)+5
xx_4 = 5*runif(1)+5; yy_4 = 5*runif(1)+5
Any hint on how can I automatizate to generate a point in a specific square?
Here's a little function that does what you ask. You tell it the size of the square (i.e. the length on one side), the number of pieces you want to cut it into (which should obviously be a square number), and the piece you want a random sample in (numbered left to right, bottom to top, as in your example).
square_sample <- function(size = 10, pieces = 4, n = 1)
{
x_min <- ((n - 1) %% sqrt(pieces)) * size/sqrt(pieces)
y_min <- ((n - 1) %/% sqrt(pieces)) * size/sqrt(pieces)
c(x = runif(1, x_min, x_min + size/sqrt(pieces)),
y = runif(1, y_min, y_min + size/sqrt(pieces)))
}
Test it out on your example: we should get a point with an x value between 5 and 10, and a y value between 0 and 5:
square_sample(size = 10, pieces = 4, n = 2)
#> x y
#> 5.968655 3.254514
Or pick the middle square of a 150 * 150 square cut into 9 pieces. Here we expect both x and y to be between 50 and 100:
square_sample(size = 150, pieces = 9, n = 5)
#> x y
#> 78.47472 97.32562
You could write a function using three parameters:
number of squares you want to go "to the right" (in your picture: 0 for squares 1&3, 2 for squares 2&4)
number of squares you want to go up (0 for squares 1&2, 2 for squares 3&4)
length of a size of your square
using these parameters, you should be able to remodify your code, replace the +0/+5 with parameter * width of the square
xx_1 = square_length*runif(1)+right_param * square_length
yy_1 = square_length*runif(1)+upwards_param * square_length
x.min = 0
x.max=10
y.min=0
y.max=10
num.random = 100
possible.squares = c(1,2,4,8,16)
squares = sample(possible.squares, 1)
x.length = x.max/squares
y.length = y.max/squares
x.coord = seq(from=x.min, to=x.max, by = x.length)
y.coord = seq(from=y.min, to=y.max, by = y.length)
set.seed(1)
loop {
n = #<which ever square you want>
x.rand = runif (1, min = x.coord[n-1], max = x.coord[n])
y.rand = runif (1, min = y.coord[n-1], max = y.coord[n])
#(x,y) is your coordinate for the random number in the nth square
}
Does this help?
you can use the absolute real part of a complex number vector, this code will generate any number of points that you want.
Npoints = 4 # any multiple of 4 will generate equal number of points in each quarterion
x = Re(1i**(1:Npoints)) %>% abs
y = Re(1i**(0:(Npoints-1))) %>% abs
randoms = lapply(1:(2*Npoints),function(x){
5*runif(1)
})%>% unlist
coor.mat =cbind(x + randoms[1:Npoints],
y + randoms[(Npoints +1) : (2*Npoints)])
Now coor.mat should be a 2 column matrix where col1 is x, col2 is y, and the number of rows is the number of points you wanted to generate.
edit: small correction
Related
I am needing to create a path between two points for which distance and degrees are known, and then convert the result to Cartesian coordinates using R. The origin for both points is 0,0. The radii always vary in length. The direction is always anti-clockwise.
I have been unable to work out how to do the degrees sequence as the range sometimes may include 0°. The start and end degrees will vary so I need method that can handle any range <= 180°. In other words, the difference between the start and end angle will never exceed 180°.
The issue is illustrated in the code below:
rad <- seq(943.0975, 939.5975, length.out = 1000)
deg <- seq(67.8352, 247.8352, length.out = 1000)
The rad vector works correctly but the deg vector is incorrect as it ends up following a clockwise direction. Doing a descending seq() means it doesn't cross 0°.
How is a range of degree values that contains 0° handled in this instance?
Unable to find a concise method, this is how I handled the issue. It involves a process that generates two vectors for degrees - one for everything to the "left" of 0° and one for everything to the "right" of 0°. It is "messy" around the 0° in that the difference between the two values directly to the left and right of 0° may not be == turni, but given the size of endturnp the issue is not perceptible. This code only works if degdiff <= 180°.
Declared variables
degA <- 67.8352 # Start point degrees
radA <- 943.0975 # Start point radius
degB <- 247.8352 # End point degrees
radB <- 939.5975 # End point radius
endturnp <- 2030 # Number of points to plot between start and end point
Derived variables
degdiff <- (outdegs - outdege + 180) %% 360 - 180 # Degrees difference between start and end
degdiff <- ifelse(degdiff < 0, degdiff * -1, degdiff) # Convert to positive number if negative
turni <- degdiff / endturnp # Get degrees increment between degA and degB based on "endturnp"
Generate degree vector from degA to degB
if(degA < degB) { # If range includes 0 degrees
leftlen <- 0:floor((360 - degB) / turni) # Return length vector for degrees > 180 & < 360
leftdeg <- sort(degB + (leftlen * turni), decreasing = T) # Calculate degrees
rightlen <- 0:floor(degA/turni) # Return length vector
rightdeg <- degA - (turni * rightlen) # calculate degree
indexdeg <- append(rightdeg,leftdeg) # Combine degrees vectors
} else {
indexdeg <- seq(degA, degB, length.out = endturnp) # Create index
}
Generate radii vector from degA to degB
indexrad <- seq(radA, radB, length.out = length(indexdeg))
Combine radii and degrees vectors and calculate xy coordinates
arcpath <- data.frame(rad = indexrad,
deg = indexdeg,
x = NA,
y = NA)
arcpath$x <- arcpath$rad * sin(pi * 2 * arcpath$deg / 360)
arcpath$y <- arcpath$rad * cos(pi * 2 * arcpath$deg / 360)
And finally, plot result
ggplot() +
geom_path(data = arcpath, aes(x = x, y = y),
linewidth = .4,
lineend = "square")
I am trying to change the value range of a variable (array, set of values) while keeping its properties. I don't know the exact name in math, but I mean such a kind of transformation that the variable array has exactly the same properties, the spacing between the values is the same, but the range is different. Maybe the code below will explain what I mean.
I just want to "linearly transpose" (or smth?) values to some other range and the distribution should remain same. In other words - I'll just change the scope of the variable using the regression equation y = a * x + b. I assume that the transformation will be completely linear, the correlation between the variables is exactly 1, and I calculate new variable (array) from a regression equation, actually a system of equations where I simply substitute the maximum ranges of both variables:
minimum.y1 = minimum.x1 * a + b
maximum.y2 = maximum.x2 * a + b
from which I can work out the following code to obtain a and b coefficients:
# this is my input variable
x <- c(-1, -0.5, 0, 0.5, 1)
# this is the range i want to obtain
y.pred <- c(1,2,3,4,5)
max_y = 5
min_y = 1
min_x = min(x)
max_x = max(x)
c1 = max_x-min_x
c2 = max_y-min_y
a.coeff = c2/c1
b.coeff = a.coeff-min_x
y = x * a.coeff + b.coeff
y
# hey, it works! :)
[1] 1 2 3 4 5
the correlation between the variable before and after the transformation is exactly 1. So we have a basis for further action. Let's get it as a function:
linscale.to.int <- function(max.lengt, vector) {
max_y = max.lengt
min_y = 1
min_x = min(vector)
max_x = max(vector)
c1 = max_x-min_x
c2 = max_y-min_y
a.coeff = c2/c1
b.coeff = a.coeff-min_x
return(vector * a.coeff + b.coeff)
}
x <- c(-1, -0.5, 0, 0.5, 1)
linscale.to.int(5,x)
[1] 1 2 3 4 5
and it works again. But here's the thing: when i aplly this function to random distribution, like this:
x.rand <- rnorm(50)
y.rand <- linscale.to.int(5,x.rand)
plot(x.rand, y.rand)
or better seable this:
x.rand <- rnorm(500)
y.rand <- linscale.to.int(20,x.rand)
plot(x.rand, y.rand)
I get the values of the second variable completely out of range; it should be between 1 and 20 but i get scope of valuest about -1 to 15:
And now the question arises - what am I doing wrong here? Where do I go wrong with such a transformation?
What you are trying to do is very straightforward using rescale from the scales package (which you will already have installed if you have ggplot2 / tidyverse installed). Simply give it the new minimum / maximum values:
x <- c(-1, -0.5, 0, 0.5, 1)
scales::rescale(x, c(1, 5))
#> [1] 1 2 3 4 5
If you want to have your own function written in base R, the following one-liner should do what you want:
linscale_to_int <- function(y, x) (x - min(x)) * (y - 1) / diff(range(x)) + 1
(Note that it is good practice in R to avoid periods in function names because this can cause confusion with S3 method dispatch)
Testing, we have:
x <- c(-1, -0.5, 0, 0.5, 1)
linscale_to_int(5, x)
#> [1] 1 2 3 4 5
x.rand <- rnorm(50)
y.rand <- linscale_to_int(5, x.rand)
plot(x.rand, y.rand)
y.rand <- linscale_to_int(20, x.rand)
plot(x.rand, y.rand)
Created on 2022-08-31 with reprex v2.0.2
I have the following code:
# package for ploting functions
using Plots
# use GR
gr()
# nb points to plot
nbPts = 22
# define polar coordinates of a 30 degree (pi/6) rotation
sine = sin(pi/6)
cosine = cos(pi/6)
# scale factor
scale_factor = 0.9
#---------------------------------------
# 1. PLOT POINTS USING ROTATION MATRIX
#---------------------------------------
# define Rotation matrix ( angle = pi/6, center = (0, 0) )
R = zeros(Float64, 2, 2)
R[1,1] = R[2,2]= cosine
R[1,2] = -sine
R[2,1] = sine
# Scale matrix
### ... <-- EXERCISE 4(c): define a uniform scaling matrix (use scale_factor)
# arrays of points coords
X_mat = zeros(nbPts)
Y_mat= zeros(nbPts)
# first Point (1,0)
X_mat[1] = 1.0
Y_mat[1] = 0.0
for i in 2:nbPts
prevPoint = [X_mat[i-1], Y_mat[i-1]]
#apply rotation to previous point to obtain new point
newPoint = R * prevPoint
### ... <-- EXERCISE 4(c): apply scaling matrix
X_mat[i] = newPoint[1]
Y_mat[i] = newPoint[2]
end
# plot points in blue
plt1 = scatter(X_mat, Y_mat, color=:blue, xlim = (-1.1, 1.1), ylim = (-1.1, 1.1), label=false, title="Rotation using matrices" );
#---------------------------------------
# 2. PLOT POINTS USING COMPLEX NUMBERS
#---------------------------------------
function ComplexProduct(z, w)
(((z[1]*w[1])+(z[2]*w[2])),((z[1]*w[2])+(z[2]*w[1])))
### ... <-- EXERCISE 4(b): implement complex product z * w
end
# first point: z = 1 + 0 * i
Z = ( 1.0, 0.0 )
# second point: w = cosine( pi/6) + sine( pi/6) * i
W = ( cosine, sine )
### ... <-- EXERCISE 4(c): apply scale_factor to W
# arrays of points coords
X_comp = zeros(nbPts)
Y_comp = zeros(nbPts)
# first Point (1,0)
X_comp[1] = Z[1]
Y_comp[1] = Z[2]
for i in 2:nbPts
prevPoint = (X_comp[i-1], Y_comp[i-1])
newPoint = ComplexProduct(prevPoint[1], prevPoint[2]) ### <-- EXERCISE 4(b): compute newPoint by applying rotation to prevPoint (use complex product)
X_comp[i] = newPoint[1]
Y_comp[i] = newPoint[2]
end
# plot points in red
plt2 = scatter(X_comp, Y_comp, color=:red, xlim = (-1.1, 1.1), ylim = (-1.1, 1.1), label=false, title="Rotation using complex numbers" );
# arrange and display
display( plot( plt1, plt2, layout = (1, 2), size=(600*2, 600) ))
The Error:
The Thing I want:
I have to implement a product of complex numbers and this should be used to calculate the rotation with complex numbers.
Should look like that:
What do I have to change so that the BoundsError is fixed?
Don't know what exactly i do wrong because of the poorly information i get from this error log.
Greetings and thanks for the help.
prevPoint[1] is a scalar while your function ComplexProduct expects something that has 2 elements. Perhaps you wanted to pass prevPoint instead of prevPoint[1]?
BTW you use incorrect naming pattern. CamelNaming is discouraged for Julia.
Your variable should be named prev_point and your function should be named complex_product.
Fixed the bug by changing the following code:
newPoint = ComplexProduct(prevPoint, W)
in line 92
I don't understand why dot product of normalized vector is always data size -1.
a <- scale(rnorm(100))
crossprod(a)
# equal = 100 - 1 = 99
b <- scale(runif(50))
crossprod(b)
# equal = 50 - 1 = 49
c <- scale(rchisq(30, 5))
crossprod(c)
# equal = 30 - 1 = 29
I want to know mathematical understanding.
Not in LaTex, but proof may help you to understand:
Your values are scaled, so: [x_i-mean(X)] / sd(X).
Crossprod does sum of squares of x_i = Sum_i ( [x_i-mean(X)])^2
Variance (squared sd): var(X) = sd^2(X) = 1/(n-1) * Sum_i ( [x_i-mean(X)])^2
Crossprod = Sum_i ([x_i-mean(X)] / sd(X))^2) = 1/sd(X)^2 * Sum_i ( [x_i-mean(X)]^2) = 1/(1/(n-1)) = n-1
I'll probably want to hit myself over the head for not getting this:
How do I generate a vector with the expected height of a normal distribution over Y bins (nbins in the below), of exactly N elements.
Like so, in the below picture:
Y or nbins = 15
N or nstat = 77
... should return something like: c(1,1,2,4, ...)
I know I could draw rnorm(77), but that'll never be exactly normal, and looping over 10.000 iterations or so seems overkill.
So I tried using qnorm for that purpose, but I have a hunch that:
sth is wrong with the below code
there has to be an easier, more elegant way
Here is what I got:
nbins <- 15
nstat <- 77
item.pos <- qnorm( # to the left of which value lies...
1:(nstat) / (nstat+1)# ... the n-statement?
# using nstat + 1 because we want midpoints, not cutoffs for later
)
bins <- cut(
x = item.pos,
breaks = nbins,
ordered_result = TRUE
)
height <- summary(bins)
height <- as.numeric(bins)
If your range of data is from -2:2 with 15 intervals and the sample size is 77 I would suggest the following to get the expected heights of the 15 intervals:
rn <- dnorm(seq(-2,2, length = 15))/sum(dnorm(seq(-2,2, length = 15)))*77
[1] 1.226486 2.084993 3.266586 4.716619 6.276462 7.697443 8.700123 9.062576 8.700123 7.697443
[11] 6.276462 4.716619 3.266586 2.084993 1.226486
The barplot of this looks like:
barplot(height = rn, names.arg = round(seq(-2, 2, length = 15), 2))
So, in your sample of 77 you would get the first value of the sequence in 1.226486, the second value in 2.084993 cases, etc. Its difficult to generate a vector as you described at the beginning, because the sequence above does not consist of integers.