I need to plot ECDF's of all columns of the dataframe in one single plot and get an x_limit on the axis too.
The function that I wrote:
library(lattice)
library(latticeExtra)
ecdf_plot <- function(data){
# Drop columns with only NA's
data <- data[, colSums(is.na(data)) != nrow(data)]
data$P_key <- NULL
ecdfplot(~ S12, data=data, auto.key=list(space='right'))
}
Problem:
The ECDF in the above function only plots for the column S12 but I need this for all columns in the dataframe. I know i can do S12 + S13 + ... but the source data changes and we don't exactly know how many and which columns will the dataframe get. Is there a better way out of this? Also, is it possible to get the x_limit for the combined plot to be just one range like xlim(0,100)?
I think this task would be easier using ggplot. It would be very easy to set the limits as required, customise the appearance, etc.
The function would look like this:
library(dplyr)
library(tidyr)
library(ggplot2)
ecdf_plot <- function(data) {
data[, colSums(is.na(data)) != nrow(data)] %>%
pivot_longer(everything()) %>%
group_by(name) %>%
arrange(value, by_group = TRUE) %>%
mutate(ecdf = seq(1/n(), 1 - 1/n(), length.out = n())) %>%
ggplot(aes(x = value, y = ecdf, colour = name)) +
xlim(0, 100) +
geom_step() +
theme_bw()
}
Now let's test it on a random data frame:
set.seed(69)
df <- data.frame(unif = runif(100, 0, 100), norm = rnorm(100, 50, 25))
ecdf_plot(df)
Related
I have a dataset with very limited data points.
x<- c(4, 8, 13, 24)
y<- c(40, 37, 28, 20)
df<- data.frame(x,y)
Now I want to extrapolate this data, creating a dataset where the value of y will be given for every value (no decimals) of x between 1-100. x and y have a linear relationship.
Secondly, could this be done for multiple dataframes by using something like a loop?
Thank you!
This is a short snippet that does this:
linear_xy <- lm(y ~ x, data = df)
# df <- broom:::augment.lm(linear_xy, newdata = complete(df, x = 1:100)) # one way
df <- df %>% # another way
complete(x = 1:100) %>%
mutate(.fitted = predict(linear_xy, newdata = .))
ggplot(df, aes(x, y)) +
geom_line(aes(y = .fitted)) +
geom_point() +
ggpubr::theme_pubr()
This requires that you have the packages {tidyverse}, {broom}, and {ggpubr} installed.
Second part
Assumming we want to do this with multiple data-frames, we have to
restructure things a bit.
x <- c(4, 8, 13, 24)
y <- c(40, 37, 28, 20)
df <- tibble(x, y)
I don't have multiple data-frames (or tibbles), so I'll make this the
primary one, and make up a function (a factory) that yields data-frames, that are a bit different from the above df.
df_factory <- . %>%
mutate(x_new = x + sample.int(100, size = n()),
x = if_else(x_new >= 100, x, x_new),
y_new = y + rnorm(n(), mean = median(y), sd = sd(y)),
y = y_new,
y_new = NULL,
x_new = NULL)
Thus df_factory is a function of one-variable, and that must be a
data-frame that has an x and y;
df1 <- df_factory(df)
df2 <- df_factory(df)
df3 <- df_factory(df)
all_dfs <- list(df1, df2, df3)
all_dfs <- bind_rows(all_dfs, .id = "df_id")
Here we ensure that the relation to the original data-frame is preserved in the all_dfs data-frame via the new variable df_id.
Next we want to:
Collapse the variables into their individual data-frame, and we put
that in a list-column named data.
For each (see rowwise) we have to perform:
An "interpolating" linear model (not a piece-wise one so...)
Predict on each of these linear_xy (which are also stored in a list-column`).
Unnest it all back, so it can be fed into ggplot as one contiguous data-frame.
all_dfs %>%
nest(data = c(x,y)) %>%
rowwise() %>%
mutate(linear_xy = list(lm(y ~ x, data = data)),
augment = list(broom:::augment.lm(linear_xy,
newdata = complete(data, x = 1:100)))) %>%
ungroup() %>%
select(-data, -linear_xy) %>%
unnest(augment) ->
all_dfs_predictions
Note: -> at the end shows what the pipe result is now assigned to.
The group informs ggplot to treat the rows as separate via their
df_id. And for fun we add the color and fill to also depend on df_id. In fact I could have choosen something else to be the coloraesthetics dependent, like "original df" vs. "others" or if a certain threshold should distinguish them, etc.. But then the group aesthetic would still tell ggplot to separate the rows amongst this relation.
ggplot(all_dfs_predictions, aes(x, y, group = df_id, color = df_id, fill = df_id)) +
geom_line(aes(y = .fitted)) +
geom_point() +
lims(x = c(1,100)) +
ggpubr::theme_pubr()
My first Q here, so please go lightly if I'm out of step anywhere.
I'm trying to code R to produce a single chart to contain a number of data series lines. The number of data series may vary but will be provided in the data frame. I have tried to rearrange another thread's content to print the geom_line , but not successfully.
The logic is:
#desire to replace loop of 1:5 with ncol(df)
print(ggplot(df,aes(x=time))
for (i in 1:5) {
print (+ geom_line(aes(y=df[,i]))
}
#functioning geom point loops ggplot production:
for (i in 1:5) {
print(ggplot(df,aes(x=time,y=df[,i]))+geom_point())
}
#functioning multi-line ggplot where n is explicit:
ggplot(data=df, aes(x=time), group=1) +
geom_line(aes(y=df$`3`))+
geom_line(aes(y=df$`4`))
The functioning example code produces n number of point charts, 5 in this case. I would like just one chart to contain n line series.
This may be similar to How to plot n dimensional matrix? for which there are currently no relevant answers
Any contributions much appreciated, thanks
You can use gather from tidyverse "world" to do that.
As you didn't supply a sample data I used mtcars.
I created two data.frames one with 3 columns one with 9. In each one of them I plotted all of the variables against the variable mpg.
library(tidyverse)
df3Columns <- mtcars[, 1:4]
df9Columns <- mtcars[, 1:10]
df3Columns %>%
gather(var, value, -mpg) %>%
ggplot(aes(mpg, value, group = var, color = var)) +
geom_line()
df9Columns %>%
gather(var, value, -mpg) %>%
ggplot(aes(mpg, value, group = var, color = var)) +
geom_line()
Edit - using the sample data in comments.
library(tidyverse)
df %>%
rownames_to_column("time") %>%
gather(var, value, -time) %>%
ggplot(aes(time, value, group = var, color = var)) +
geom_line()
Sample data:
df <- structure(list("39083" = c(96, 100, 100), "39090" = c(99, 100, 100), "39097" = c(99, 100, 100)), row.names = 3:5, class = "data.frame")
To strictly answer your question, you can simply store your ggplot in a variable and add the geom_line one by one:
df <- structure(list("39083" = c(96, 100, 100), "39090" = c(99, 100, 100), "39097" = c(99, 100, 100)), row.names = 3:5, class = "data.frame")
g <- ggplot(df, aes(x = 1:nrow(df)))
for (i in colnames(df))
{
g <- g + geom_line(y = df[,i])
}
g <- g + scale_y_continuous(limits = c(min(df), max(df)))
print(g)
However, this is not a very convenient solution. I would highly recommend to refactor your data frame to be more ggplot style.
df.ultimate <- data.frame(time = numeric(), value = numeric(), group = character())
for (i in colnames(df))
{
df.ultimate <- rbind(df.ultimate, data.frame(time = 1:nrow(df), value = df[, i], group = i))
}
g <- ggplot(df.ultimate, aes(x = time, y = value, color = group))
g <- g + geom_line()
print(g)
A one-line solution:
ggplot(data.frame(time = rep(1:nrow(df), ncol(df)),
value = as.vector(as.matrix(df)),
group = rep(colnames(df), each = nrow(df))),
aes(x = time, y = value, color = group)) + geom_line()
In R, I would like to create a box plot that also shows all data points. There are numerous posts and websites where you can find this information, but they all seem to show the data points in ‘jitter’ or ‘random’ style. Here is an example code using the ToothGrowth dataset with ggplot2 in R.
library(datasets)
data(ToothGrowth)
ToothGrowth$dose <- as.factor(ToothGrowth$dose)
library(ggplot2)
ggplot(ToothGrowth, aes(x=dose, y=len)) +
geom_boxplot(notch = TRUE) +
geom_jitter(position=position_jitter(0.2))
However, I would like to have the data points ordered from the lowest at the lower-left to highest at the top-right. Please see example in this link:
https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3966983/figure/F1/ (freely accessible). Specifically, I refer to Figure 1a, top portion (‘Purity’).
Might anyone have suggestions? I would much appreciate it. Thank you.
I don't know if this is what you are after but maybe you can inspire yourself from the code below.
set.seed(1234)
n <- 20
x <- rnorm(n)
boxplot(x)
points(seq(0.75, 1.25, length.out = n), sort(x))
df1 <- sapply(1:4, function(i) rnorm(n, mean = i))
df1 <- as.data.frame(df1)
df1 <- reshape2::melt(df1)
boxplot(value ~ variable, df1)
sp <- split(df1, df1$variable)
for(i in 1:4){
points(seq(i - 0.25, i + 0.25, length.out = n), sort(sp[[i]]$value))
}
Edit.
A ggplot2 solution uses a similar trick to define the points' x axis coordinates. The only thing "strange", is to rely on R's internal representation of factors as consecutive integers starting at 1. Note that this must be seen as a hack, but as a reliable one, I don't believe it will ever change.
library(ggplot2)
library(tidyverse)
df1 %>%
group_by(variable) %>%
arrange(value) %>%
mutate(xcoord = seq(-0.25, 0.25, length.out = n())) %>%
ggplot(aes(x = variable, y = value, group = variable)) +
geom_boxplot() +
geom_point(aes(x = xcoord + as.integer(variable)))
I could not find an answer / a solution to the following question:
I have two numeric variables. I take the sum of both and want to bar plot the relative frequency of that summed variable + indicate the proportion of its sub components (i.e. the mean proportion of one variable as part of the sum).
Example: I have v1 = number questions and v2 = number of answers. Each observation can have x questions and y answers and x+y interactions.
Example code:
df <- data.frame(matrix(ncol = 2, nrow = 5))
x <- c("questions", "answers")
colnames(df) <- x
df$questions <- c(1,2,3,1,2)
df$answers <- c(2,3,4,2,3)
df$interactionsum <- df$questions + df$answers
ggplot(df, aes(x = interactionsum)) +
geom_bar(aes(y = (..count..)/sum(..count..))) +
ylab("Relative frequencies") +
xlab("Sum of interactions")
In this data setting, one third of the first bar would be questions (mean proportion) and two thirds answers (mean proportion). How can I achieve this type of grouping with ggplot2?
Thank you in advance!
df <- data.frame(matrix(ncol = 2, nrow = 5))
x <- c("questions", "answers")
colnames(df) <- x
df$questions <- c(1,2,3,1,2)
df$answers <- c(2,3,4,2,3)
df$interactionsum <- df$questions + df$answers
require(dplyr)
require(tidyr)
require(ggplot2)
df<-df %>% group_by(interactionsum) %>%
summarize(questions=mean(questions)/mean(interactionsum) ,answers=mean(answers)/mean(interactionsum) , n=n()/nrow(df) ) %>% mutate(interactionsum=as.factor(interactionsum)) %>%
gather("key","means",questions, answers)
ggplot(df,aes(x=interactionsum,y=means*n,fill=key))+geom_bar(stat="identity")
For each possible interaction sum, we create the mean of all its questions variable and the mean of all its answer variable. Then we gather then (using tidyr) to make the long data format favoured by ggplot, then we plot those means in a stacked bar using the "identity" statistic, since they already reflect the frequency in the value.
I also turned interaction sum into a factor to improve the way it looks in the end result.
# example data
df = data.frame(questions = c(1,2,3,1,2),
answers = c(2,3,4,2,3))
df$interactionsum <- df$questions + df$answers
library(tidyverse)
df %>%
group_by(interactionsum) %>%
summarise_all(sum) %>%
gather(x,y,-interactionsum) %>%
group_by(interactionsum) %>%
mutate(y = y/sum(y)) %>%
ggplot(aes(interactionsum, y, fill=x))+
geom_bar(stat="identity")
I have large data sets where I am doing exploratory screenings for correlations. I want to do a correlation test to identify significantly related variables, and then plot these variables against each other.
data <- data.frame(a = 1:10, b = c(1.5*(1:9), 10), c = 2*(1:10), d = sample(1:5, 10, replace = T))
cor_data <- corr.test(data)
sig_cor <- ifelse(cor_data$p <0.05, cor_data$r, NA)
sig_cor_long <- sig_cor %>%
data.frame() %>%
mutate(var1 = rownames(sig_cor)) %>%
gather(var2, value = r, -var1) %>%
drop_na(r) %>%
filter(r != 1)
This identifies pairs a-b and b-c as significantly correlated, so I want to plot those. How can I automate this process of selecting the paired variables from sig_cor_long to plot via ggplot from data? An example plot that I want to create for each correlated pair would be:
ggplot(data, aes(a, b)) +
geom_smooth(method = 'lm')+
geom_point(shape = 21, color = 'darkblue', fill = 'white')
I want to have a function to input into ggplot to tell it to plot all the var1 and var2 pairs identified in sig_cor_long for which the raw data are in data.
Ok so, this is one way of plotting e.g. all the plots where there is significant correlation (in a list, so you could do anything with them)
do.call(gridExtra::grid.arrange,
ifelse(cor_data$p <0.05, cor_data$r, NA) %>%
as.data.frame() %>%
rownames_to_column() %>%
gather(pair, val, -rowname) %>%
drop_na() %>%
filter(val != 1) %$%
map2(rowname, pair, ~ggplot() + geom_smooth(method = "lm", aes(data[, .x], data[, .y])) + geom_point(aes(data[, .x], data[, .y])))
)