marginal effects in R ("margins") - r

I'm trying to plot the results of margin command (Average Marginal Effects) and the order of variables on the plot doesn't match the order of labels (for one label I get a value of another variable). For ggplot everything is ok (although it uses summary). Can anyone explain what is going on and how to make a proper plot? I'd be grateful :)
library(ggplot2)
library(tibble)
library(broom)
library(margins)
library(Ecdat)
data(Participation)
?Participation
logit_participation = glm(lfp ~ ., data = Participation, family = "binomial")
tidy(logit_participation)
summary(logit_participation)
effects_logit_participation = margins(logit_participation)
print(effects_logit_participation)
summary(effects_logit_participation)
plot(effects_logit_participation)
effects_logit_participation = summary(effects_logit_participation)
ggplot(data = effects_logit_participation) +
geom_point(aes(factor, AME)) +
geom_errorbar(aes(x = factor, ymin = lower, ymax = upper)) +
geom_hline(yintercept = 0) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45))

Related

How can I rename the variables used for a graph only for that graph?

I hope this comes as an easy question for some of you.
I am basically plotting the Average Marginal Effects of several variables based on a logistic regression:
summary(AMElogmodel1 <- glm(Smartphone ~ sf_group + sf_sex + age + sf_marital + sf_big + sf_hhsize,
data = Selectsfinal, family = binomial(link = "logit"), na.action = na.exclude))
And then, in order to have the plot with ggplot instead of the default AME plot for this cases I did this (mainly because I wanted to revert the X and Y axis):
xxx <- summary(margins(AMElogmodel1))
ggplot(data = xxx) +
geom_point(aes(factor, AME)) +
geom_errorbar(aes(x = factor, ymin = lower, ymax = upper)) +
geom_hline(yintercept = 0) +
coord_flip() +
theme_minimal() +
labs(x='Variable', y ="Average marginal effect")
Leading to this graph:
As you can see, the names displayed there are the technical names for the variables and their categories. I imagine that if I change their names and labels this would work, but then it would mess with all my other code. Is there a way to change the names? So instead of sf_sexMale, Male and instead of age66-76, Age 66-76?
Also, is there a way to change the order in which they are displayed?
Thank you very much for your help.

Adding a regression trend line and a shaded standard error area to a ggplot for regression models that geom_smooth does not handle

I have a data.frame with observed success/failure outcomes per two groups along with expected probabilities:
library(dplyr)
observed.probability.df <- data.frame(group = c("A","B"), p = c(0.4,0.6))
expected.probability.df <- data.frame(group = c("A","B"), p = qlogis(c(0.45,0.55)))
observed.data.df <- do.call(rbind,lapply(c("A","B"), function(g)
data.frame(group = g, value = c(rep(0,1000*dplyr::filter(observed.probability.df, group != g)$p),rep(1,1000*dplyr::filter(observed.probability.df, group == g)$p)))
)) %>% dplyr::left_join(expected.probability.df)
observed.probability.df$group <- factor(observed.probability.df$group, levels = c("A","B"))
observed.data.df$group <- factor(observed.data.df$group, levels = c("A","B"))
I'm fitting a logistic regression (binomial glm with a logit link function) to these data with the offset term:
fit <- glm(value ~ group + offset(p), data = observed.data.df, family = binomial(link = 'logit'))
Now, I'd like to plot these data as a bar graph using ggplot2's geom_bar, color-coded by group, and to add to that the trend line and shaded standard error area estimated in fit.
I'd use stat_smooth for that but I don't think it can handle the offset term in it's formula, so looks like I need to resort to assembling this figure in an alternative way.
To get the bars and the trend line I used:
slope.est <- function(x, ests) plogis(ests[1] + ests[2] * x)
library(ggplot2)
ggplot(observed.probability.df, aes(x = group, y = p, fill = group)) +
geom_bar(stat = 'identity') +
stat_function(fun = slope.est,args=list(ests=coef(fit)),size=2,color="black") +
scale_x_discrete(name = NULL,labels = levels(observed.probability.df$group), breaks = sort(unique(observed.probability.df$group))) +
theme_minimal() + theme(legend.title = element_blank()) + ylab("Fraction of cells")
So the question is how to add to that the shaded standard error around the trend line?
Using stat_function I am able to shade the entire area from the upper bound of the standard error all the way down to the X-axis:
ggplot(observed.probability.df, aes(x = group, y = p, fill = group)) +
geom_bar(stat = 'identity') +
stat_function(fun = slope.est,args=list(ests=coef(fit)),size=2,color="black") +
stat_function(fun = slope.est,args=list(ests=summary(fit)$coefficients[,1]+summary(fit)$coefficients[,2]),geom='area',fill="gray",alpha=0.25) +
scale_x_discrete(name = NULL,labels = levels(observed.probability.df$group), breaks = sort(unique(observed.probability.df$group))) +
theme_minimal() + theme(legend.title = element_blank()) + ylab("Fraction of cells")
Which is close but not quite there.
Any idea how to subtract from the shaded area above the area that's below the lower bound of the standard error? Perhaps geom_ribbon is the way to go here, but I don't know how to combine it with the slope.est function

Transfer calculated value from stat_smooth to other geom like linerange

I have a question about ggplot2.
I want to connect data point with ols result via vertical line, like the code listed below.
Can I transfer ..y.., the value calculated by stat_smooth, to geom_linerange directly?
I tried stat_smooth(..., geom = "linerange", mapping(aes(ymin=pmin(myy, ..y..), ymax=pmax(myy,..y..)) but it is not the result I want.
library(ggplot2)
df <- data.frame(myx = 1:10,
myy = c(1:10) * 5 + 2 * rnorm(10, 0, 1))
lm.fit <- lm("myy~myx", data = df)
pred <- predict(lm.fit)
ggplot(df, aes(myx, myy)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE) +
geom_linerange(mapping = aes(ymin = pmin(myy, pred),
ymax = pmax(myy, pred)))
stat_smooth evaluates the values at n evenly spaced points, with n = 80 by default. These points may not coincide with the original x values in your data frame.
Since you are calculating predicted values anyway, it would probably be more straightforward to add that back to your data frame and plot all geom layers based on that as your data source, for example:
df$pred <- pred
ggplot(df, aes(myx, myy)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE) +
geom_linerange(aes(ymin = myy, ymax = pred))

ggplot2: geom_pointrange() facet_grid() with coord_flip() and free scales

I am trying to generate a graph with the estimates and confidence intervals from the same regression for a number of countries. I ran the regressions using dplyr's group_by(country), and then I aggregated all the results into a data frame with broom's tidy().
When creating the graph from this data frame (called bycountry1), I run the following code:
ggplot(bycountry1, aes(x = country, y = estimate, ymin = estimate - std.error * 2, ymax = estimate + std.error * 2)) +
geom_hline(yintercept = 0, colour = "black", lty = 2) +
geom_pointrange() +
coord_flip() + facet_grid(. ~ term, scales = "free")
This is what I want, except that I'd like to have the scales for each box to be different, so that all of them would look more like the religious1box. Since that is the one with most variability, it dominates the scale, and then in most of the other boxes you cannot see the variance. As the code above shows, I did indicate scales = "free" in facet_grid() and I tried all the variants, also with facet_wrap(), and I cannot get this to work.
Following the suggestion of aosmith, I made it work using geom_errorbarh and removing coord_flip(). I also had to set the height of the geom_errorbarh to 0 and add a geom_point for the estimate. Here is the code:
ggplot(bycountry1, aes(y = country, x = estimate, xmin = estimate - std.error * 2, xmax = estimate + std.error * 2)) +
geom_vline(xintercept = 0, colour = "black", lty = 2) +
geom_point() +
geom_errorbarh(height = 0) +
facet_grid(. ~ term, scales = "free")
And the resulting image

loess and glm plotting with ggplot2

I am trying to plot the model predictions from a binary choice glm against the empirical probability using data from the titanic. To show differences across class and sex I am using faceting, but I have two things things I can't quite figure out. The first is that I'd like to restrict the loess curve to be between 0 and 1, but if I add the option ylim(c(0,1)) to the end of the plot, the ribbon around the loess curve gets cut off if one side of it is outside the bound. The second thing I'd like to do is draw a line from the minimum x-value (predicted probability from the glm) for each facet, to the maximum x-value (within the same facet) and y = 1 so as to show glm predicted probability.
#info on this data http://biostat.mc.vanderbilt.edu/wiki/pub/Main/DataSets/titanic3info.txt
load(url('http://biostat.mc.vanderbilt.edu/wiki/pub/Main/DataSets/titanic3.sav'))
titanic <- titanic3[ ,-c(3,8:14)]; rm(titanic3)
titanic <- na.omit(titanic) #probably missing completely at random
titanic$age <- as.numeric(titanic$age)
titanic$sibsp <- as.integer(titanic$sibsp)
titanic$survived <- as.integer(titanic$survived)
training.df <- titanic[sample(nrow(titanic), nrow(titanic) / 2), ]
validation.df <- titanic[!(row.names(titanic) %in% row.names(training.df)), ]
glm.fit <- glm(survived ~ sex + sibsp + age + I(age^2) + factor(pclass) + sibsp:sex,
family = binomial(link = "probit"), data = training.df)
glm.predict <- predict(glm.fit, newdata = validation.df, se.fit = TRUE, type = "response")
plot.data <- data.frame(mean = glm.predict$fit, response = validation.df$survived,
class = validation.df$pclass, sex = validation.df$sex)
require(ggplot2)
ggplot(data = plot.data, aes(x = as.numeric(mean), y = as.integer(response))) + geom_point() +
stat_smooth(method = "loess", formula = y ~ x) +
facet_wrap( ~ class + sex, scale = "free") + ylim(c(0,1)) +
xlab("Predicted Probability of Survival") + ylab("Empirical Survival Rate")
The answer to your first question is to use coord_cartesian(ylim=c(0,1)) instead of ylim(0,1); this is a moderately FAQ.
For your second question, there may be a way to do it within ggplot but it was easier for me to summarize the data externally:
g0 <- ggplot(data = plot.data, aes(x = mean, y = response)) + geom_point() +
stat_smooth(method = "loess") +
facet_wrap( ~ class + sex, scale = "free") +
coord_cartesian(ylim=c(0,1))+
labs(x="Predicted Probability of Survival",
y="Empirical Survival Rate")
(I shortened your code slightly by eliminating some default values and using labs.)
ss <- ddply(plot.data,c("class","sex"),summarise,minx=min(mean),maxx=max(mean))
g0 + geom_segment(data=ss,aes(x=minx,y=minx,xend=maxx,yend=maxx),
colour="red",alpha=0.5)

Resources