using Reduce/do.call with ifelse - r

This is purely a curiosity (learning more about Reduce). There are way better methods to achieve what I'm doing and I am not interested in them.
Some people use a series of nested ifelse commands to recode/look up something. Maybe it looks like this:
set.seed(10); x <- sample(letters[1:10], 300, T)
ifelse(x=="a", 1,
ifelse(x=="b", 2,
ifelse(x=="c", 3,
ifelse(x=="d", 4, 5))))
Is there a way to use either do.call or Reduce with the ifelse to get the job done a little more eloquently?

Try this:
> library(gsubfn)
> strapply(x, ".", list(a = 1, b = 2, c = 3, d = 4, 5), simplify = TRUE)
[1] 5 4 5 5 1 3 3 3 5 5 5 5 2 5 4 5 1 3 4 5 5 5 5 4 5 5 5 3 5 4 5 1 2 5 5 5 5
[38] 5 5 5 3 3 1 5 3 2 1 5 2 5 4 5 3 5 2 5 5 5 4 5 1 2 5 4 5 5 5 5 1 3 1 5 5 5
[75] 1 5 4 5 3 3 5 5 3 5 3 1 5 3 2 2 5 5 5 5 4 5 3 5 5 1 4 1 4 5 5 5 5 5 5 5 5
[112] 5 2 5 5 5 3 5 5 5 2 4 4 5 3 3 5 4 5 5 5 1 5 3 4 3 5 5 2 5 5 3 1 5 2 5 5 5
[149] 1 5 5 2 1 2 4 2 2 3 5 2 5 5 5 5 5 3 5 5 5 5 5 5 5 5 5 5 2 3 5 4 4 2 5 5 5
[186] 5 5 5 5 2 1 1 1 5 5 5 5 3 5 5 3 5 5 5 2 5 5 5 3 5 5 5 5 5 1 5 5 5 5 2 2 5
[223] 5 5 4 3 4 5 5 4 5 5 5 3 5 3 5 5 5 5 4 5 5 1 5 5 2 5 5 5 2 5 5 3 2 5 4 5 2
[260] 5 5 3 5 5 1 4 3 5 4 5 2 5 5 3 5 5 5 5 5 1 1 5 2 5 1 5 5 5 5 5 5 5 5 5 5 5
[297] 5 1 5 2

Here is an attempt. It is neither beautiful nor does it use ifelse:
f <- function(w,s) {
if(is.null(s$old))
w$output[is.na(w$output)] <- s$new
else
w$output[w$input==s$old] <- s$new
return(w)
}
set.seed(10); x <- sample(letters[1:10], 300, T)
subst <- list(
list(old="a", new=1),
list(old="b", new=2),
list(old="c", new=3),
list(old="d", new=4),
list(old=NULL, new=5)
)
workplace <- list(
input=x,
output=rep(NA, length(x))
)
Reduce(f, subst, workplace)

Related

Frequency Table of Categorical Variables as a Data Frame in R

I would like to create a frequency Table of all Categorical Variables as a Data Frame in R. I would like to find the frequency and percentage of each survey response (grouped by condition, as well as the total frequency). I would like to generate this as a data frame.
An example of the desired frequency count out for just ONE variable ("q1"). I want a similar freq count for most of the variables in my data:
I have data such as this. The actual data has many more categorical variables.
library(readr)
data_in <- read_table2("treatment_cur q13_3 q14_1 q14_2 q14_3 q14_4 q14_5 q14_6 q14_7 q14_8 q14_9 q14_10 q14_11 q14_12 q14_13 q14_14 q14_15
Control 3 2 3 6 5 6 6 6 4 5 5 5 4 6 6 5
Control 2 4 5 6 5 6 5 5 6 4 5 5 6 5 4 6
Treatment 3 1 2 6 4 6 5 4 6 4 6 1 5 6 4 6
Control 3 2 3 6 4 6 6 6 6 6 6 6 6 5 5 6
Control NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
Control 4 6 5 6 5 6 5 6 6 5 1 1 6 5 5 6
Control 3 3 2 2 3 3 6 6 4 6 5 5 3 6 6 2
Treatment 2 3 2 3 1 3 1 1 1 3 3 3 3 3 3 1
Control 3 5 5 6 3 6 3 3 3 2 2 1 4 2 3 4
Control 2 1 1 1 1 1 4 4 1 1 1 1 1 4 4 2
Control 4 3 4 6 6 6 6 6 6 6 6 6 6 6 6 6
Control 4 2 6 6 4 6 5 6 6 5 6 5 6 6 6 6
Control 2 2 3 3 2 3 5 6 5 3 3 3 3 5 3 2
Control 3 2 4 3 4 5 4 4 5 3 3 5 4 5 5 4
Treatment 2 2 2 2 2 3 1 1 2 2 3 2 3 3 2 3
Control 4 3 3 3 5 6 6 6 6 6 6 6 6 6 6 6
Treatment 2 1 3 3 2 1 3 4 2 2 3 3 2 3 3 3
Treatment 4 2 6 4 4 2 3 5 4 5 1 1 5 4 4 5
Control 3 3 3 4 4 4 4 5 3 2 5 4 5 5 4 4
Control 4 6 6 6 6 6 6 6 6 6 6 6 5 6 6 5
Control 2 2 3 6 2 5 1 2 4 4 1 1 6 4 4 6
Treatment 4 3 3 6 6 6 6 6 6 6 6 6 6 6 6 6
Treatment 4 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
Treatment 1 1 2 4 4 4 1 1 1 1 1 1 6 1 1 6
Treatment 3 2 3 3 2 6 6 6 6 3 3 2 4 5 5 6
Control 2 1 1 1 1 1 1 2 1 1 1 1 1 2 2 1
Control 1 3 3 3 1 1 5 5 2 4 5 5 4 1 2 5
Treatment 3 4 4 5 5 4 4 4 3 5 3 4 4 6 6 5
Control NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
Control 2 2 4 6 2 4 2 2 3 5 4 4 4 3 3 5
Treatment 1 1 2 1 1 1 1 1 6 1 1 1 6 2 3 6
Treatment 2 6 1 4 4 1 1 2 2 2 1 2 1 2 2 2
Treatment 3 3 4 4 4 6 6 5 4 6 3 5 5 6 6 4
Treatment 2 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3
Control 4 3 4 6 4 6 4 5 6 3 4 4 6 6 4 6
Control 4 4 3 6 2 5 2 2 4 3 1 6 5 5 5 5
Control NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
Treatment 2 3 3 6 5 6 1 2 6 5 4 4 5 5 5 6
Control 4 6 6 6 6 6 5 5 5 5 5 6 5 5 5 5
Treatment 2 1 1 3 1 3 4 4 4 4 1 4 3 4 4 4
Treatment 2 1 3 3 3 3 4 6 5 4 5 5 4 6 6 5
Control 4 6 6 6 6 6 5 5 5 6 6 5 5 5 6 6
Control NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
Control 4 2 2 4 2 4 6 6 6 6 4 6 5 6 6 5
Control 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
Treatment 3 4 2 5 5 5 6 5 5 5 5 5 5 6 6 6
Control NA 2 4 4 4 4 4 3 4 6 4 5 4 6 4 4
Control 2 2 2 3 1 3 4 1 1 1 2 1 3 3 3 3
Treatment 2 2 2 3 2 2 3 3 2 2 2 2 2 2 2 2
Control 3 3 3 6 6 6 6 6 6 6 5 6 6 6 6 6
Treatment 2 1 2 2 2 1 2 2 1 1 2 1 2 2 1 3
Treatment 4 5 5 6 6 5 5 6 5 5 4 5 5 4 4 5
Control 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
Treatment 3 3 4 4 4 6 3 2 5 3 2 2 5 6 5 6
Control 4 4 3 3 6 3 6 6 3 2 4 4 4 4 4 4
Treatment 4 1 3 4 4 4 5 6 6 6 6 6 6 6 6 6
Control 4 4 5 6 5 5 4 6 6 6 6 5 6 6 6 6
Treatment 3 3 4 6 6 6 6 6 5 6 6 5 4 6 6 4
Control 4 4 6 6 4 6 6 6 6 4 4 3 5 6 6 6
Control 4 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
Treatment 4 5 5 6 6 6 6 6 5 5 6 6 5 5 6 6
Treatment 4 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
Control 2 1 2 1 1 1 1 3 1 4 4 1 1 1 1 1
Treatment 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
Treatment 4 6 5 5 5 5 5 6 5 4 5 4 4 5 5 4
Treatment 4 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
Control 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
Treatment 4 5 6 6 6 5 6 6 6 5 6 6 6 6 6 6
Control 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
Treatment 3 3 2 5 4 4 5 6 6 4 5 5 4 5 4 6
Treatment 4 5 4 4 4 5 5 6 4 5 4 3 6 6 6 6
Control 1 2 3 2 1 4 1 1 3 1 3 3 3 3 4 4
Control 3 6 6 6 6 6 5 1 5 6 5 6 6 6 6 6
Control 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
Control 4 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
")
My current solution is too complicated. If I wanted to know the frequency of variables from q13_3:q14_9, I know that I can do something like this to find it:
library(tables)
varList <- 2:11
data_in[varList] <- lapply(data_in[varList], factor,exclude = NULL)
lapply(varList,function(x,df,byVar){
tabular((Factor(df[[x]],paste(colnames(df)[x])) + 1) ~ ((Factor(df[[byVar]],paste(byVar)))*((n=1) + Percent("col"))),
data= df)
},data_in,"treatment_cur")
Below is a snippet of what my current output looks like. The problem is that the output is a list of a list which cannot be exported into a single excel sheet. I have to manually copy everything from the console onto an excel file.
treatment_cur
Control Treatment
q14_8 n Percent n Percent
1 6 13.953 4 12.50
2 4 9.302 4 12.50
3 5 11.628 2 6.25
4 6 13.953 4 12.50
5 5 11.628 7 21.88
6 13 30.233 11 34.38
NA 4 9.302 0 0.00
All 43 100.000 32 100.00
[[10]]
treatment_cur
Control Treatment
q14_9 n Percent n Percent
1 6 13.953 4 12.50
2 6 13.953 4 12.50
3 4 9.302 4 12.50
4 6 13.953 5 15.62
5 5 11.628 8 25.00
6 12 27.907 7 21.88
NA 4 9.302 0 0.00
All 43 100.000 32 10
This works alright, but I want to:
Find the total frequency of each variable value as well (treatment + condition) as an additional column (as seen in the image above);
I do not like the function I am using to produce this output. I want to export this into an excel file, but since this output is actually a list of lists (it cannot be exported to excel), and I am finding it quite cumbersome to copy and paste these values from the console into excel. I would like an easier way of finding these frequencies! Surely R has a better way of doing this...
Any help is MUCH appreciated!!
One way to do this would be to explore using the gtsummary package.
using your code above you can produce a table quite easily with counts and percentages:
library(gtsummary)
library(readr)
library(flextable)
tbl_summary(data_in, by = "treatment_cur") %>%
add_overall() %>%
as_flex_table() %>%
flextable::save_as_docx(., path = "G:/test.docx")
If you just run:
tbl_summary(data_in, by = "treatment_cur") %>%
add_overall()
you will see the table it generates for you. The extra code after that makes it so that it is able to be exported to a docx file. From there you can copy that into excel. This generates the counts you requested and you can determine if it is a simpler implementation.
Another alternative is to write directly to a csv file:
tbl_summary(data_in, by = "treatment_cur") %>%
add_overall() %>%
as_tibble() %>%
readr::write_csv( .,path = "G:/test.csv")
OR
if you really need everything in separate columns you can separate the n and percents into two tables, merge them and then write to csv.
#keep counts only
ncount <- tbl_summary(data_in, by = "treatment_cur",
statistic = all_categorical()~ "{n}") %>%
add_overall()
#keep pcts only
pctdata <- tbl_summary(data_in, by = "treatment_cur",
statistic = all_categorical()~ "{p}%") %>%
add_overall()
#combine and output
tbl_merge(list(ncount, pctdata)) %>%
as_tibble() %>%
readr::write_csv(., "G:/test2.csv")
Edit:
Another way to approach this is with the janitor package. You can adorn counts and percentages pretty easily and merge the datasets together. After that it is easy to export to a csv/Excel. One downside here is you have to loop through your variables to get a table for each and then combine them together, however the code below is a good start to create it:
library(janitor)
datatry <- data_in %>%
janitor::tabyl( q13_3,treatment_cur) %>%
adorn_totals("col") %>%
adorn_totals("row")
datatry2 <- data_in %>%
janitor::tabyl( q13_3,treatment_cur) %>%
janitor::adorn_percentages(denominator = 'col') %>%
adorn_totals("row") %>%
adorn_totals("col") %>%
mutate(Total = ifelse(is.na(q13_3), Total, ifelse(q13_3 == 'Total',1, Total)))
datatry3 <- inner_join(datatry, datatry2, by = 'q13_3') %>%
mutate(variable ='q13_3')
Assuming that you constructed data_in as above:
library(dplyr)
library(purrr)
# reformat
tt <- data_in$treatment_cur
data_in$treatment_cur <- NULL
data_in %>% map(function(a)
{
ret <- data.frame(Treatment.n=rep(0, 6), Control.n=rep(0, 6))
b <- table(a[tt=="Treatment"])
ret[names(b), "Treatment.n"] <- b
b <- table(a[tt=="Control"])
ret[names(b), "Control.n"] <- b
ret$Treatment.percent <- ret$Treatment.n / sum(ret$Treatment.n)
ret$Control.percent <- ret$Control.n / sum(ret$Control.n)
ret
}) %>% do.call(what=cbind)
It assumes answers data is \in 1..6 and NA are ignored.

spatstat deleting marks from a point pattern and subseuently the points

I am looking at the point pattern data set in spatstat anemones which has 231 points with marks attached to them which define the diameter.. I want to delete the marks and the points within the point pattern when the diameter is equal to 2
Here is the data:
>
[1] 6 4 4 6 3 3 5 3 5 4 4 6 5 3 4 7 4 6 6 5 4 4 5 3 3 6 4 5 4 4 5 3 3 5
3 4 5 8 5 4 6 5 6 4 5 3 3 4 5 6 4 4 3 4 4 6 5 4 3 6 5 3 [63] 3 6 5 3
3 2 5 7 4 4 4 3 3 4 3 6 2 6 6 3 4 3 7 6 3 4 2 7 4 5 4 4 4 6 4 3 3 3 3
6 7 3 7 3 2 4 3 5 2 3 4 4 3 3 3 6 3 4 5 3 6 3 [125] 7 5 3 3 4 4 5 4 4
6 5 3 3 3 5 3 6 5 5 4 4 3 4 4 4 4 3 4 7 4 6 5 7 6 3 6 5 4 6 4 5 4 5 3
6 3 3 6 4 6 4 4 6 3 5 3 4 6 5 5 4 5 [187] 4 3 3 4 4 4 4 5 4 5 5 5 4 6
4 4 5 3 5 4 3 4 4 4 3 4 5 5 3 3 5 3 4 5 6 2 5 2 3 2 3 3 7 5 4
thanks!
Another solution is to use the generic R command subset:
X <- subset(anemones, marks != 2)
From the question it is not quite clear whether you want to get rid of all the marks after deleting these points. In that case use unmark:
X <- unmark(X)
Correct me if I'm wrong but I'm reading this as meaning you wish to remove observations when anemones$marks is equal to 2.
If so this should do it:
updated_anemones <- anemones[!anemones$marks == 2,]

recoding variable returns "undefined columns selected"

I have spent a couple hours trying to figure this out:
I need to reverse-code a variable. I have 3 identical variables at 3 differnet timepoints. The first 2 are easily recoded, but the third returns "undefined columns selected", even though I copy-paste the code. I also tried different versions of the same code, and have the same problem.
When I do print(dat$cuppass) it returns printed values, with 1 NA. I had similarly 1 or 2 NAs with the previous 2 timepoints, and have not had a problems when applying complete.cases.
Could anyone figure out how to recode 3d variable?
Thank you!!
these work:
dat$cdppassr <- 5-dat$cdppass[complete.cases(dat[dat$cdppass])]
print(dat$cdppassr)
dat$pdppassr <- 5-dat$pdppass[complete.cases(dat[dat$pdppass])]
print(dat$pdppassr)
Neither this,
dat$cuppassr <- 5-dat$cuppass[na.omit(dat[dat$cuppass])]
nor this works:
dat$cuppassr <- recode[na.omit(dat$cuppass,'0=5; 1=4; 2=3; 3=2; 4=1; 5=0')]
> print(cuppass)<br/>
[1] 5 5 5 5 4 3 4 3 3 3 5 4 5 3 5 5 3 3 5 3 3 3 3 4 3 3 4 2<br/>
[29] 3 3 5 2 3 2 5 3 3 4 5 3 4 5 3 5 4 5 5 4 3 5 3 5 3 4 3 5<br/>
[57] 4 5 4 4 4 3 3 5 4 4 5 5 3 4 4 4 3 3 3 5 5 5 3 5 3 4 3 3<br/>
[85] 2 4 3 3 4 5 2 5 5 3 4 4 3 5 5 5 3 5 5 5 3 5 5 3 5 3 5 5<br/>
[113] 4 5 5 3 3 5 3 2 5 5 5 5 4 5 3 5 4 5 5 5 5 4 5 5 3 5 5 5<br/>
[141] 3 5 5 5 3 3 2 5 4 2 3 5 4 5 5 3 3 5 5 5 3 3 5 5 5 NA 3 3<br/>
[169] 5 5 5 5 5 3 5 5 3 5 5 4 5 4 3 5 4 3 2 5 3 3 4 3 5 2 4 5<br/>
[197] 5 3 5 4 4 4 4 2 5 5 4 5 3 2 4 5 4 5 5 5 3 3 5 5 5 3 3 5<br/>
[225] 5 4 2 5 5 3 5 5 3 3 5 4 5 5 0 3 3 5 3 3 4 3 3 1 5 5 5 5<br/>
[253] 5 3 4 5 4 5 5 5 3 5 5 2 5 5 2 3 5 4 5 5 5 3 4 3 3 5 3 3<br/>
[281] 3 3 5 5 3 3 2 5 4 5 3 4 3 4 3 5 4 3 5 5 3 5 5 3 5 5 5 5<br/>
[309] 5 3 5 5 5 5 5 5 4 5 5 3 4 4 5 5 5 5 3 5 3 5 3 5 5 5 5 2<br/>
[337] 3 0 3 3 5 3 3 5 3 4 4 5 5 5 5 3 5 5 3 4 3 5 4 5 4 5 3 2<br/>
[365] 3 3 5 3 5 5 3 4 4 5 5 5 4 5 3 5 3 3 5 3 5 3 5 3 3 5 5 2<br/>
[393] 3 3 5 5 4 3 5 4 3 3 4 3<br/>

From table to data.frame

I have a table that looks like:
dat = data.frame(expand.grid(x = 1:10, y = 1:10),
z = sample(LETTERS[1:3], size = 100, replace = TRUE))
tabl <- with(dat, table(z, y))
tabl
y
z 1 2 3 4 5 6 7 8 9 10
A 5 3 1 1 3 6 3 7 2 4
B 4 5 3 6 5 1 3 1 4 4
C 1 2 6 3 2 3 4 2 4 2
Now how do I transform it into a data.frame that looks like
1 2 3 4 5 6 7 8 9 10
A 5 3 1 1 3 6 3 7 2 4
B 4 5 3 6 5 1 3 1 4 4
C 1 2 6 3 2 3 4 2 4 2
Here are a couple of options.
The reason as.data.frame(tabl) doesn't work is that it dispatches to the S3 method as.data.frame.table() which does something useful but different from what you want.
as.data.frame.matrix(tabl)
# 1 2 3 4 5 6 7 8 9 10
# A 5 4 3 1 1 3 3 2 6 2
# B 1 4 3 4 5 3 4 4 3 3
# C 4 2 4 5 4 4 3 4 1 5
## This will also work
as.data.frame(unclass(tabl))

What is the Matlab/Octave equivalent or R's 'merge' (or 'expand.grid')?

I am looking for the Matlab way of doing the following:
> merge(2:4,3:7)
x y
1 2 3
2 3 3
3 4 3
4 2 4
5 3 4
6 4 4
7 2 5
8 3 5
9 4 5
10 2 6
11 3 6
12 4 6
13 2 7
14 3 7
15 4 7
> expand.grid(2:4,3:7)
Var1 Var2
1 2 3
2 3 3
3 4 3
4 2 4
5 3 4
6 4 4
7 2 5
8 3 5
9 4 5
10 2 6
11 3 6
12 4 6
13 2 7
14 3 7
15 4 7
I usually do it with meshgrid:
>> [x y] = meshgrid(2:4, 3:7);
>> [x(:) y(:)]
ans =
2 3
2 4
2 5
2 6
2 7
3 3
3 4
3 5
3 6
3 7
4 3
4 4
4 5
4 6
4 7
Use ndgrid for n variables (2 and more). For example (4-D space)
[X,Y,Z,T] = ndgrid(2:4, 3:7, 1:2, 1:10);

Resources