简体   繁体   中英

generate circular boundary for points R

I'm working to setup boundaries around 4 distinct regions of interest in an image - the image & data are in 0-1 normalized space, with four equally sized quadrants. It is easy to setup square boundaries:

df.all$ROI <- ifelse(df.all$x_pred_normalised > 0 & df.all$x_pred_normalised <= .5 &
                     df.all$y_pred_normalised > 0 & df.all$y_pred_normalised <= .5, c("lower_left"), 
              ifelse(df.all$x_pred_normalised > 0 & df.all$x_pred_normalised <= .5 &
                     df.all$y_pred_normalised > .5 & df.all$y_pred_normalised <= 1, c("upper_left"),
              ifelse(df.all$x_pred_normalised > .5 & df.all$x_pred_normalised <= 1 &
                     df.all$y_pred_normalised > 0 & df.all$y_pred_normalised <= .5, c("lower_right"), 
              ifelse(df.all$x_pred_normalised > .5 & df.all$x_pred_normalised <= 1 &
                     df.all$y_pred_normalised > .5 & df.all$y_pred_normalised <= 1, c("upper_right"),
                     c("other")))))

However, I would like to setup a circular boundary (radius of.15 as an example) at the center of each quadrant (.25, .25; .25, .75; .75, .25; .75, .75) that provides the same output.

sample data:

x_pred_normalized <- rnorm(1000, mean=.5, sd=.5)
y_pred_normalized <- rnorm(1000, mean=.5, sd=.5)
df.all <- as.data.frame(cbind(x_pred_normalized, y_pred_normalized))

df.all$ROI_square <- ifelse(df.all$x_pred_normalised > 0 & df.all$x_pred_normalised <= .5 &
                     df.all$y_pred_normalised > 0 & df.all$y_pred_normalised <= .5, c("lower_left"), 
              ifelse(df.all$x_pred_normalised > 0 & df.all$x_pred_normalised <= .5 &
                     df.all$y_pred_normalised > .5 & df.all$y_pred_normalised <= 1, c("upper_left"),
              ifelse(df.all$x_pred_normalised > .5 & df.all$x_pred_normalised <= 1 &
                     df.all$y_pred_normalised > 0 & df.all$y_pred_normalised <= .5, c("lower_right"), 
              ifelse(df.all$x_pred_normalised > .5 & df.all$x_pred_normalised <= 1 &
                     df.all$y_pred_normalised > .5 & df.all$y_pred_normalised <= 1, c("upper_right"),
                     c("other")))))

desired output:

x_pred_normalized y_pred_normalized ROI_square  ROI_circle
.99               .99               upper_right other
.51               .51               upper_right other
.25               .25               lower_left  lower_left

Your example does not result in data that you describe. AND your should NEVER use cbind inside a call to data.frame unless there's a dataframe object as one of the arguments. Here's a dataframe construction that does meet your description (and is small enough not to fill up your console with useless output when testing):

df.all <- data.frame(
             x_pred_normalized = pmax( 0, pmin( rnorm(10, mean=.5, sd=.5), 0.99999)), 
             y_pred_normalized = pmax( 0, pmin( rnorm(10, mean=.5, sd=.5), 0.99999)) )
# all values between 0 and .99999

I find deeply nested ifelse constructions very confusing to debug and wasteful of space, whereas using a two column matrix to select items from a character matrix to be easy to understand. The columns in the indexing numeric matrix are constructed with findInterval so it will be vectorized and much more efficient in both time and space.

 df.all$quad <- matrix(c("First", "Third","Second","Fourth"), ncol=2)[ cbind(
                    findInterval(df.all$x_pred_normalized,c(  0, .5, 1,Inf) ),
                    findInterval(df.all$y_pred_normalized,c(  0, .5,1,Inf) ) )]
#--------------
> df.all
   x_pred_normalized y_pred_normalized   quad
1         0.78886879         0.3063979  Third
2         0.48122193         0.5440334 Second
3         0.85496224         0.4849327  Third
4         0.01457113         0.5116597 Second
5         0.76936612         0.7424495 Fourth
6         0.99999000         0.4544564  Third
7         0.55473184         0.0000000  Third
8         0.99967092         0.7843946 Fourth
9         0.72561140         0.3985747  Third
10        0.17755530         0.1337408  First

For the circle-containment column I would use a logical tests multiplied by an indexing value that could be used to extract a value from a vector via Boolean arithmetic:

dist= function(x,y) 
df.all$circle <- with( df.all, c("other", "First", "Second", "Third", "Fourth")[
      1 +  # value when all subsequent tests are FALSE
      1*(sqrt( (x_pred_normalized-0.25)^2+(y_pred_normalized-0.25)^2) <=0.25) + 
       # above should be should be 1 (TRUE) for lower-rt;  else 0
      2*(sqrt( (x_pred_normalized-0.25)^2+ (y_pred_normalized-0.75)^2) <=0.25)+
      3*(sqrt( (x_pred_normalized-0.75)^2+(y_pred_normalized-0.25)^2) <=0.25)+
      4*(sqrt( (x_pred_normalized-0.75)^2+(y_pred_normalized-0.75)^2)<=0.25) ] )
#--------------
> df.all
   x_pred_normalized y_pred_normalized   quad circle
1         0.78886879         0.3063979  Third  Third
2         0.48122193         0.5440334 Second  other
3         0.85496224         0.4849327  Third  other
4         0.01457113         0.5116597 Second  other
5         0.76936612         0.7424495 Fourth Fourth
6         0.99999000         0.4544564  Third  other
7         0.55473184         0.0000000  Third  other
8         0.99967092         0.7843946 Fourth  other
9         0.72561140         0.3985747  Third  Third
10        0.17755530         0.1337408  First  First

I see that I used a circular radius of 0.25 whereas you asked for 0.15. The necessary changes should be easy to implement.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM