How rounding up starting at .6 (not at .5)?
For example, round(53.51245, 4) will return me 53.5125, but I want 53.5124.
How can I specify a separation number (namely increase the values starting from .6)?
I'm not sure if this is a duplicate of the post linked to in the comments (but the post may certainly be relevant). From what I understand OP would like to "round" values up or down if they are >= 0.6 or < 0.6, respectively. (The linked post refers to the number of digits a number should be rounded to, which is a different issue.)
In response to OPs question, here is an option where we define a custom function my.round
my.round <- function(x, digits = 4, val = 0.6) {
z <- x * 10^digits
z <- ifelse(signif(z - trunc(z), 1) >= val, trunc(z + 1), trunc(z))
z / 10^digits
}
Then
x <- 53.51245
my.round(x, 4)
#[1] 53.5124
x <- 53.51246
my.round(x, 4)
#[1] 53.5125
my.round is vectorised, so we could have done
my.round(c(53.51245, 53.51246, 53.51246789), digits = 4)
#[1] 53.5124 53.5125 53.5125
Related
When plotting the ratio between two variables, their relative order is often of no concern, yet depending on which variable is in the numerator, its relative size is constrained either to (0,1) or (1, Inf), which is somewhat unintuitive and breaks symmetry. I want to plot ratios "symmetrically", without resorting to symmetric log-scale, by having a y-axis that goes like 1/4, 1/3, 1/2, 1, 2, 3, 4 or, equivalently, 4^-1, 3^-1, 2^-1, 1, 2, 3, 4 in regular intervals. I've come up with the following:
symmult <- function(x){
isf <- is.finite(x) & (x>0)
xf <- x[isf]
xf <- ifelse(xf>=1,
xf-1,
1-(1/xf))
x[isf] <- xf
x[!isf] <- NA
x[!is.finite(x)] <- NA
return(x)
}
symmultinv <- function(x){
isf <- is.finite(x)
xf <- x[isf]
xf <- ifelse(x[isf]>=0,
x[isf]+1,
-1/(x[isf]-1))
x[isf] <- xf
x[!isf] <- NA
x[!is.finite(x)] <- NA
return(x)
}
sym_mult_trans = function(){trans_new("sym_mult", symmult, symmultinv )}
x <- c(-4:-2, 1:4)
x[x<1] <- 1/abs(x[x<1])
ggplot() +
geom_point(aes(x=x, y=x)) +
scale_y_continuous(trans="sym_mult")
The transformation works, but I cannot get the axis labels etc. to work for any 0<x<1, without setting them manually. Any help would be greatly appreciated.
You can create bespoke 'breaks' and 'format' functions that you can use inside trans_new (or pass to scale_y_continuous directly via its breaks and labels parameters).
For the breaks function, remember it will take as input a length-two numeric vector representing the range of the y axis. You must then convert this to a number of appropriate breaks. Here, if the minimum of the range is less than one, we take its reciprocal, find the pretty breaks between one and that number, then take the reciprocal of the output. We concatenate that onto pretty breaks between 1 and our range maximum:
# Define breaks function
symmult_breaks <- function(x) {
c(1 / extended_breaks(5)(c(1/x[x < 1], 1)),
extended_breaks(5)(c(1, x[x >= 1])))
}
For the labelling function, remember, it needs to take as input the vector of numbers produced by our breaks function. We can paste a 1/ in front of the reciprocal of numbers less than one, but leave numbers of 1 or more unaltered:
# Define labelling function
symmult_labs <- function(x) {
labs <- character(length(x))
labs[x >= 1] <- as.character(x[x >= 1])
labs[x < 1] <- paste("1", as.character(1/x[x < 1]), sep = "/")
labs
}
So your full new transformation becomes:
# Use our four functions to define the whole transformation:
sym_mult_trans <- function() {
trans_new(name = "sym_mult",
transform = symmult,
inverse = symmultinv,
breaks = symmult_breaks,
format = symmult_labs)
}
And your plot becomes:
ggplot() +
geom_point(aes(x = x, y = x)) +
scale_y_continuous(trans = "sym_mult")
Is there a function in R that can take a vector as input and output a vector with local mins and maxes, and where they occur in the original vector?
Let v be a plain vector and define a peak as an element which is strictly larger than the elements to either side and similarly for trough.
1) rollapply Then the following gives two logical vectors each having the same length as v. One indicates positions of peaks using TRUE and FALSE for other positions and the other indicates troughs in the same manner. which(peaks) and which(troughs) can be used to get the index numbers if that representation is preferred.
library(zoo)
peaks <- rollapply(v, 3, function(x) x[2] > max(x[-2]), fill = FALSE)
troughs <- rollapply(v, 3, function(x) x[2] < min(x[-2]), fill = FALSE)
We could combine them like this where each output component is 1 if it is a peak, -1 if it is a trough and 0 otherwise.
extreme <- function(x) (x[2] > max(x[-2])) - (x[2] < min(x[-2]))
rollapply(v, 3, extreme, fill = FALSE)
2) Base R A base R method would be:
prev <- c(NA, v[-length(v)])
post <- c(v[-1], NA)
(v > pmax(prev, post)) - (v < pmin(prev, post))
I am using Jenks Natural Breaks via the BAMMtools package to segment my data in RStudio Version 1.0.153. The output is a vector that shows where the natural breaks occur in my data set, as such:
[1] 14999 41689 58415 79454 110184 200746
I would like to take the output above and create the ranges inferred by the breaks. Ex: 14999-41689, 41690-58415, 58416-79454, 79455-110184, 110185-200746
Are there any functions that I can use in R Studio to accomplish this? Thank you in advance!
Input data
x <- c(14999, 41689, 58415, 79454, 110184, 200746)
If you want the ranges as characters you can do
y <- x; y[1] <- y[1] - 1 # First range given in question doesn't follow the pattern. Adjusting for that
paste(head(y, -1) + 1, tail(y, -1), sep = '-')
#[1] "14999-41689" "41690-58415" "58416-79454" "79455-110184" "110185-200746"
If you want a list of the actual sets of numbers in each range you can do
seqs <- Map(seq, head(y, -1) + 1, tail(y, -1))
You can definitely create your own function that produces the exact output you're looking for, but you can use the cut function that will give you something like this:
# example vector
x = c(14999, 41689, 58415, 79454, 110184, 200746)
# use the vector and its values as breaks
ranges = cut(x, x, dig.lab = 6)
# see the levels
levels(ranges)
#[1] "(14999,41689]" "(41689,58415]" "(58415,79454]" "(79454,110184]" "(110184,200746]"
Having trouble understanding numeric matching / indexing in R.
If I have a situation where I create a dataframe such as:
options(digits = 3)
x <- seq(from = 0, to = 5, by = 0.10)
TestDF <- data.frame(x = x, y = dlnorm(x))
and I wanted to compare a hardcoded value to my y column -
> TestDF[TestDF$y == 0.0230,]$x
numeric(0)
That being said, if I compare to the value that's straight out of the dataframe (which for an x value of 4.9, should be a y value of 0.0230).
> TestDF[TestDF$y == TestDF[50,]$y,]$x
[1] 4.9
Does this have to do with exact matching? If I limit the digits to 3 decimal point, then 0.0230000 won't be the same as the original value in y I'm comparing to? If this is the case, is there a way around it if I do need to extract values based on rounded, hard-coded values?
You can use round() function to reduce the number of decimal digits to the preferred scale of the floating point number. See below.
set.seed(1L)
x <- seq(from = 0, to = 5, by = 0.10)
TestDF <- data.frame(x = x, y = dlnorm(x))
constant <- 0.023
TestDF[ with(TestDF, round(y, 3) == constant), ]
# x y
# 50 4.9 0.02302884
You can compare the rounded y with the stated value:
> any(TestDF$y == 0.0230)
[1] FALSE
> any(round(TestDF$y, 3) == 0.0230)
[1] TRUE
I'm not certain you grok the meaning of the digits option. From ?options it says about digits
digits: controls the number of significant digits to print when printing numeric values.
(emphasis mine.) So this only affects how the values are printed, not how they are stored.
You generated a set of reals, none of which are exactly 0.0230. This has nothing to do with exact matching. The value you indicated should be 0.0230 is actually stored as
> with(TestDF, print(y[50], digits = 22))
[1] 0.02302883835550340041465
regardless of the digits setting in options because that setting only affects the printed value. And the issue is not exact matching because even with the small fudge allowed by the recommended way to do comparisons, all.equal(), y[50] and 0.0230 are still not equal
> with(TestDF, all.equal(0.0230, y[50]))
[1] "Mean relative difference: 0.001253842"
Has anyone come up with a solution to adjust rounded R values shown in a knitr document, either as stand along \Sexpr{} or through xtable? Typing?round returns Note that for rounding off a 5, the IEC 60559 standard is expected to be used, ‘go to the even digit’.
My problem is the following scenario when showing calculated numbers from a dataframe using xtable. If the values were each shown in a separate column in a table, the reader would assume there is a calculation error:
2.5 + 3.1 = 5.6
would show up as
2 + 3 = 6
when R rounds the numbers (I have set the significant digits to 0 since the audience doesn't need more detail). This situation could potentially happen no matter how many decimal places are shown (and I would like to avoid showing any!).
I use the following for inline expressions, however I rarely insert a number into the paragraph and it usually isn't shown as a calculation. This will show 1 decimal place for numbers less than 10 and greater than -10 and should round up on even numbers ending with 0.5.
number_hook <- function(x) {
if (is.numeric(x)) {
if (x < 10 & x > 0 | x < 0 & x > -10) {
y = prettyNum(x,
small.mark = ".",
digits = 2)
return(y)
} else if (sign(x) == 1) {
y = x + 0.5
y = trunc(y)
y = prettyNum(y, big.mark = ",", small.mark = ".", digits = 0)
return(y)
} else if (sign(x) == -1) {
y = x - 0.5
y = trunc(y)
y = prettyNum(y, big.mark = ",", small.mark = ".", digits = 0)
return(y)
}
} else {
x
}
}
Any help, work-arounds, or suggestions are appreciated! Thank you!
I have also visited this similar question.
My original recommendation did not work correctly. First you should modify your original function.
number_hook <- function(x) {
ifelse(abs(x) < 10 & abs(x) > 0, prettyNum(x, small.mark = ",", digits = 2), trunc(x))
}
This should simplify the number of if statements. Then you can use:
xtable::xtable(dplyr::mutate_if(iris, is.numeric, number_hook))
To apply the function to every numeric column in your data frame.
Try it on:
foo <- data.frame(a = rnorm(10), b = rnorm(10, 10), c = rnorm(10, -10))
xtable::xtable(dplyr::mutate_if(foo, is.numeric, number_hook))
And you should get values that you need.