Adjust binwidth size for faceted dotplot with free y axis - ggplot2

I would like to adjust the binwidth of a faceted geom_dotplot while keeping the dot sizes the same.
Using the default binwidth (1/30 of the data range), I get the following plot:
library(ggplot2)
df = data.frame(
t = c(1, 1, 1, 1, 1, 2, 2, 2, 2, 2),
x = 1,
y = c(1, 2, 3, 4, 5, 100, 200, 300, 400, 500)
)
ggplot(df, aes(x=x, y=y)) +
geom_dotplot(binaxis="y", stackdir="center") +
facet_wrap(~t, scales="free_y")
However, if I change the binwidth value, the new value is taken as an absolute value (and not the ratio of the data range), so the two facets get differently sized dots:
geom_dotplot(binaxis="y", stackdir="center", binwidth=2) +
Is there a way to adjust binwidth so it is relative to its facet's data range?

One option to achieve your desired result would be via multiple geom_dotplots which allows to set the binwidth for each facet separately. This however requires some manual work to compute the binwidths so that the dots are the same size for each facet:
library(ggplot2)
y_ranges <- tapply(df$y, factor(df$t), function(x) diff(range(x)))
binwidth1 <- 2
scale2 <- binwidth1 / (y_ranges[[1]] / 30)
binwidth2 <- scale2 * y_ranges[[2]] / 30
ggplot(df, aes(x=x, y=y)) +
geom_dotplot(data = ~subset(.x, t == 1), binaxis="y", stackdir="center", binwidth = binwidth1) +
geom_dotplot(data = ~subset(.x, t == 2), binaxis="y", stackdir="center", binwidth = binwidth2) +
facet_wrap(~t, scales="free_y")

Related

geom_bar for total counts of binned continuous variable

I'm really struggling to achieve what feels like an incredibly basic geom_bar plot. I would like the sum of y to be represented by one solid bar (with colour = black outline) in bins of 10 for x. I know that stat = "identity" is what is creating the unnecessary individual blocks in each bar but can't find an alternative to achieving what is so close to my end goal. I cheated and made the below desired plot in illustrator.
I don't really want to code x as a factor for the bins as I want to keep the format of the axis ticks and text rather than having text as "0 -10", "10 -20" etc. Is there a way to do this in ggplot without the need to use summerise or cut functions on the raw data? I am also aware of geom_col and sat_count options but again, can't achive my desired outcome.
DF as below, where y = counts at various values of a continuous variable x. Also a factor variable of type.
y = c(1 ,1, 3, 2, 1, 1, 2, 1, 1, 1, 1, 1, 4, 1, 1,1, 2, 1, 2, 3, 2, 2, 1)
x = c(26.7, 28.5, 30.0, 34.8, 35.0, 36.4, 38.6, 40.0, 42.1, 43.7, 44.1, 45.0, 45.5, 47.4, 48.0, 57.2, 57.8, 64.2, 65.0, 66.7, 68.0, 74.4, 94.1)
type = c(rep("Type 1", 20), "Type 2", rep("Type 1", 2))
df<-data.frame(x,y,type)
Bar plot of total y count for each bin of x - trying to fill by total of type, but getting individual proportions as shown by line colour = black. Would like total for each type in each bar.
ggplot(df,aes(y=y, x=x))+
geom_bar(stat = "identity",color = "black", aes(fill = type))+
scale_x_binned(limits = c(20,100))+
scale_y_continuous(expand = c(0, 0), breaks = seq(0,10,2)) +
xlab("")+
ylab("Total Count")
Or trying to just have the total count within each bin but don't want the internal lines in the bars, just the outer colour = black for each bar
ggplot(df,aes(y=y, x=x))+
geom_col(fill = "#00C3C6", color = "black")+
scale_x_binned(limits = c(20,100))+
scale_y_continuous(expand = c(0, 0), breaks = seq(0,10,2)) +
xlab("")+
ylab("Total Count")
Here is one way to do it, with previous data transformation and geom_col:
df <- df |>
mutate(bins = floor(x/10) * 10) |>
group_by(bins, type) |>
summarise(y = sum(y))
ggplot(data = df,
aes(y = y,
x = bins))+
geom_col(aes(fill = type),
color = "black")+
scale_x_continuous(breaks = seq(0,100,10)) +
scale_y_continuous(expand = c(0, 0),
breaks = seq(0,10,2)) +
xlab("")+
ylab("Total Count")

How to add count (n) / summary statistics as a label to ggplot2 boxplots?

I am new to R and trying to add count labels to my boxplots, so the sample size per boxplot shows in the graph.
This is my code:
bp_east_EC <-total %>% filter(year %in% c(1977, 2020, 2021, 1992),
sampletype == "groundwater",
East == 1,
#EB == 1,
#N59 == 1,
variable %in% c("EC_uS")) %>%
ggplot(.,aes(x = as.character(year), y = value, colour = as.factor(year))) +
theme_ipsum() +
ggtitle("Groundwater EC, eastern Curacao") +
theme(plot.title = element_text(hjust = 0.5, size=14)) +
theme(legend.position = "none") +
labs(x="", y="uS/cm") +
geom_jitter(color="grey", size=0.4, alpha=0.9) +
geom_boxplot() +
stat_summary(fun.y=mean, geom="point", shape=23, size=2) #shows mean
I have googled a lot and tried different things (with annotate, with return functions, mtext, etc), but it keeps giving different errors. I think I am such a beginner I cannot figure out how to integrate such suggestions into my own code.
Does anybody have an idea what the best way would be for me to approach this?
I would create a new variable that contained your sample sizes per group and plot that number with geom_label. I've generated an example of how to add count/sample sizes to a boxplot using the iris dataset since your example isn't fully reproducible.
library(tidyverse)
data(iris)
# boxplot with no label
ggplot(iris, aes(x = Species, y = Sepal.Length, fill = Species)) +
geom_boxplot()
# boxplot with label
iris %>%
group_by(Species) %>%
mutate(count = n()) %>%
mutate(mean = mean(Sepal.Length)) %>%
ggplot(aes(x = Species, y = Sepal.Length, fill = Species)) +
geom_boxplot() +
geom_label(aes(label= count , y = mean + 0.75), # <- change this to move label up and down
size = 4, position = position_dodge(width = 0.75)) +
geom_jitter(alpha = 0.35, aes(color = Species)) +
stat_summary(fun = mean, geom = "point", shape = 23, size = 6)

Stack bars with percentages and values shown

Here is my dataframe - data_long1
data.frame(
value = c(88, 22, 100, 12, 55, 17, 10, 2, 2),
Subtype = as.factor(c("lung","prostate",
"oesophagus","lung","prostate","oesophagus","lung",
"prostate","oesophagus")),
variable = as.factor(c("alive","alive",
"alive","dead","dead","dead","uncertain","uncertain",
"uncertain"))
)
The following code gives me a nice graph that I want, with all the values displayed, but none in percentages.
ggplot(data_long1, aes(x = Subtype, y = value, fill = variable)) + geom_bar(stat = "identity") +
geom_text(aes(label= value), size = 3, hjust = 0.1, vjust = 2, position = "stack")
What I am looking for is a stacked bar chart with The actual values displayed on the Y Axis not percentages(like previous graph) BUT also a percentage figure displayed on each subsection of the actual Bar Chart. I try this code and get a meaningless graph with every stack being 33.3%.
data_long1 %>% count(Subtype, variable) %>% group_by(Subtype) %>% mutate(pct= prop.table(n) * 100) %>% ggplot() + aes(x = Subtype, y = variable, fill=variable) +
geom_bar(stat="identity") + ylab("Number of Patients") +
geom_text(aes(label=paste0(sprintf("%1.1f", pct),"%")), position=position_stack(vjust=0.5)) + ggtitle("My Tumour Sites") + theme_bw()
I cannot seem to find a way to use the mutate function to resolve this problem. Please help.
I would pre-compute the summaries you want. Here is the proportion within each subtype:
data_long2 <- data_long1 %>%
group_by(Subtype) %>%
mutate(proportion = value / sum(value))
ggplot(data_long2, aes(x = Subtype, y = value, fill = variable)) +
geom_bar(stat = "identity") +
geom_text(aes(label= sprintf('%0.0f%%', proportion * 100)), size = 3, hjust = 0.1, vjust = 2, position = "stack")
You can also get the proportion across all groups and types simply by removing the group_by statement:
data_long2 <- data_long1 %>%
mutate(proportion = value / sum(value))
ggplot(data_long2, aes(x = Subtype, y = value, fill = variable)) +
geom_bar(stat = "identity") +
geom_text(aes(label= sprintf('%0.0f%%', proportion * 100)), size = 3, hjust = 0.1, vjust = 2, position = "stack")

Stacked Bar Chart Labels-- Using geom_text to label % on a value based y-axis

I am looking to create a stacked bar chart where my y-axis measures the value but the table shows the % of total bar.
I think I need to add a pct column to my table then use that but am not sure how to get the pct column either.
Df for example is:
date, type, value, pct
Jan 1, A, 5, 45% (5/11)
Jan 1, B, 6, 55% (6/11)
table and chart image
Maybe something like this?
library(dplyr)
library(ggplot2)
test.df <- data.frame(date = c("2020-01-01", "2020-01-01", "2020-01-02", "2020-01-02"),
type = c("A", "B", "A", "B"),
val = c(5:6, 1, 7))
test.df <- test.df %>%
group_by(date) %>%
mutate(
type.num = as.numeric(type),
prop = val/sum(val),
y_text_pos = ifelse(type=="B", val, sum(val))) %>%
ungroup()
ggplot(data = test.df, aes(x = as.Date(date), y = val, fill = type)) +
geom_col() +
geom_text(aes(y = y_text_pos, label = paste0(round(prop*100,1), "%")), color = "black", vjust = 1.1)
With the output:

Plotting facetgrid plots in seaborn with smoothing

I have a pandas dataframe a snippet of which is shown below:-
I wish to recreate the graphs shown below in Seaborn. These graphs were created in R using ggplot, but I am working with pandas/matplotlib/seaborn.
Essentially the graphs summarize the variables(mi,steps,st...) grouped by sensor id, with hours to the event on the x-axis. Additionally and most importantly, there is smoothing performed by stat_smooth() within ggplot. I have included a snippet of my ggplot code.
step.plot <- ggplot(data=cdays, aes(x=dfc, y=steps, col=legid)) +
ggtitle('time to event' +
labs(x="Days from event", y='Number of steps') +
stat_smooth(method='loess', span=0.2, formula=y~x) +
geom_vline(mapping=aes(xintercept=0), color='blue') +
theme(legend.position="none")
here is how I would do it. Bear in mind that I had to make assumptions about the structure of your data, so please review what I did before applying it.
Creating some simulated data
subject = np.repeat(np.repeat([1, 2, 3, 4, 5], 4), 31)
time = np.tile(np.repeat(np.arange(-15, 16, 1), 4), 5)
sensor = np.tile([1, 2, 3, 4], 31*5)
measure1 = subject*20 + time*(5-sensor) - time**2*(sensor-2)*0.1 + (time >= 0)*np.random.normal(100*(sensor-2), 10, 620) + np.random.normal(0, 10, 620)
measure2 = subject*10 + time*(2-sensor) - time**2*(sensor-4)*0.1 + (time >= 0)*np.random.normal(50*(sensor-1), 10, 620) + np.random.normal(0, 8, 620)
measure3 = time**2*(sensor-1)*0.1 + (time >= 0)*np.random.normal(50*(sensor-3), 10, 620) + np.random.normal(0, 8, 620)
measure4 = time**2*(sensor-1)*0.1 + np.random.normal(0, 8, 620)
Putting it in a long form dataset for plotting
df = pd.DataFrame(dict(subject=subject, time=time, sensor=sensor, measure1=measure1,
measure2=measure2, measure3=measure3, measure4=measure4))
df = pd.melt(df, id_vars=["sensor", "subject", "time"],
value_vars=["measure1", "measure2","measure3", "measure4"],
var_name="measure")
Creating the plot, without smoothing
g = sns.FacetGrid(data=df, col="measure", col_wrap=2)
g.map_dataframe(sns.tsplot, time="time", value="value", condition="sensor", unit="subject", color="deep")
g.add_legend(title="Sensor Number")
g.set_xlabels("Days from Event")
g.set_titles("{col_name}")
plt.show()
Plotted data, before smoothing
Now let's use statsmodels to smooth the data.
Please review this part, this is where I made assumptions about the sampling unit (I assume that the sampling unit is the subject, and therefore treat sensors and measure types as conditions).
from statsmodels.nonparametric.smoothers_lowess import lowess
dfs = []
for sens in df.sensor.unique():
for meas in df.measure.unique():
# One independent smoothing per Sensor/Measure condition.
df_filt = df.loc[(df.sensor == sens) & (df.measure == meas)]
# Frac is equivalent to span in R
filtered = lowess(df_filt.value, df_filt.time, frac=0.2)
df_filt["filteredvalue"] = filtered[:,1]
dfs.append(df_filt)
df = pd.concat(dfs)
Plotted data, after smoothing
From there you can tweak your plot however you like. Tell me if you have any question.