Reputation: 121
I have a dataset which contains the responses of multiple participants to emotional faces. The participants responded with which emotion they think they saw. There were multiple trials per participant. Imagine, there were 4 answer possibilities: Disgust, Sadness, Anger, and Neutral. I want to calculate the proportion each participant selected each answer possibility. When I recently asked a similar question, I got provided with a solution, which later turned out to be incomplete for my needs. Therefore, I opened this new question.
Here is some example data:
Response <- c("Disgust", "Sadness", "Disgust", "Anger", "Anger", "Neutral", "Anger", "Disgust", "Happiness") #create example data
ResponseNum <- c(1,2,1,3,3,4,3,1,5) #Response, but expressed in Numbers
ppnum <- c(1,1,1,2,2,2,3,3,3)
df2a_anger <- as.data.frame(cbind(Response, ResponseNum, ppnum)) #create dataframe
df2a_anger$ResponseNum <- as.numeric(as.character(df2a_anger$ResponseNum)) # make numeric
Here is some example code:
library(dplyr)
df2a_anger %>%
count(ppnum, ResponseNum) %>%
group_by(ppnum) %>%
mutate(n = n/sum(n))
An alternative I found myself trying to solve my problem involves the aggregate function, however both suffer the same problem: They do not count when an response option has not been chosen: For example, the code outputs that participant 1 chose Disgust 66% and Sadness 33%. I also want it to output that participant 1 chose neutral and angry for 0%. Does anyone have any idea how to get R to do that?
Upvotes: 0
Views: 317
Reputation: 7405
You could use janitor
:
library(janitor)
library(dplyr)
df2a_anger %>%
tabyl(Response, ppnum) %>%
adorn_percentages(denominator = 'col') %>%
adorn_pct_formatting()
Which gives you:
Response 1 2 3
Anger 0.0% 66.7% 33.3%
Disgust 66.7% 0.0% 33.3%
Happiness 0.0% 0.0% 33.3%
Neutral 0.0% 33.3% 0.0%
Sadness 33.3% 0.0% 0.0%
Or, if you want the response options as column headers:
df2a_anger %>%
tabyl(ppnum, Response) %>%
adorn_percentages(denominator = 'row') %>%
adorn_pct_formatting()
Which gives you:
ppnum Anger Disgust Happiness Neutral Sadness
1 0.0% 66.7% 0.0% 0.0% 33.3%
2 66.7% 0.0% 0.0% 33.3% 0.0%
3 33.3% 33.3% 33.3% 0.0% 0.0%
Upvotes: 1
Reputation: 2894
The basic table()
function should suffice in this case.
Tip: If you combine character vectors and numeric vectors to a data.frame, don't use cbind(), as this forces all columns to the same atomic object class. Use the data.frame()
function to combine them piecewise.
Response <- c("Disgust", "Sadness", "Disgust", "Anger", "Anger", "Neutral", "Anger", "Disgust", "Happiness") #create example data
ResponseNum <- c(1,2,1,3,3,4,3,1,5) #Response, but expressed in Numbers
ppnum <- c(1,1,1,2,2,2,3,3,3)
df2a_anger <- data.frame("Response" = Response,
"ResponseNum" = ResponseNum,
"Pnum" = ppnum) #create dataframe
table(df2a_anger$Pnum,df2a_anger$Response)
table(df2a_anger$Pnum,df2a_anger$ResponseNum)
absolutes = as.matrix(table(df2a_anger$Pnum,df2a_anger$Response))
proportions = absolutes/rowSums(absolutes)
barplot(t(proportions),legend.text = colnames(proportions),beside=T,ylim=c(0,1),xlab="Respondent")
This code will give you two tables, one with the absolute counts per respondent and the other with proportions.
And, as a bonus, it gives you this plot:
Upvotes: 0
Reputation: 34601
One way is to use forcats::fct_count()
:
library(dplyr)
library(forcats)
df2a_anger %>%
mutate(Response = factor(Response)) %>%
group_by(ppnum) %>%
summarise(res = fct_count(Response, prop = TRUE))
`summarise()` ungrouping output (override with `.groups` argument)
# A tibble: 15 x 4
ppnum f n p
<chr> <fct> <int> <dbl>
1 1 Anger 0 0
2 1 Disgust 2 0.667
3 1 Happiness 0 0
4 1 Neutral 0 0
5 1 Sadness 1 0.333
6 2 Anger 2 0.667
7 2 Disgust 0 0
8 2 Happiness 0 0
9 2 Neutral 1 0.333
10 2 Sadness 0 0
11 3 Anger 1 0.333
12 3 Disgust 1 0.333
13 3 Happiness 1 0.333
14 3 Neutral 0 0
15 3 Sadness 0 0
Upvotes: 1