Returning anonymous functions from lapply - what is going wrong?
R passes promises, not the values themselves. The promise is forced when it is first evaluated, not when it is passed, and by that time the index has changed if one uses the code in the question. The code can be written as follows to force the promise at the time the outer anonymous function is called and to make it clear to the reader:
pl <- lapply(1:3, function(y) { force(y); function(x) pow(x,y) } )
Anonymous function in lapply
Instead of going over the data frame directly you could switch things around and have lapply go over a vector of the column names,
data(iris)
lapply(colnames(iris), function(x) c(class(iris[[x]]), x))
or over an index for the columns, referencing the data frame.
lapply(1:ncol(iris), function(x) c(class(iris[[x]]), names(iris[x])))
Notice the use of both single and double square brackets.iris[[n]]
references the values of the n
th object in the list iris
(a data frame is just a particular kind of list), stripping all attributes, making something like mean(iris[[1]])
possible.iris[n]
references the n
th object itself, all attributes intact, making something like names(iris[1])
possible.
Using anonymous functions with lapply/sapply in R?
You have a few options here, let's start with aggregate
- not what you asked for but I want to keep your attention high ;)
aggregate(saleprice ~ car_names, cars1, max)
# car_names saleprice
#1 Corvette 72000
#2 Golf 22000
#3 Malibu 33000
Returns a data.frame (which you can easily split
if you need a list)
aggregate
is similar to tapply
coming next
tapply(cars1$saleprice, cars1$car_names, FUN = max)
#Corvette Golf Malibu
# 72000 22000 33000
Or try by
and which.max
by(cars1, cars1$car_names, FUN = function(x) x[which.max(x$saleprice), ])
#cars1$car_names: Corvette
# car_names saleprice
#3 Corvette 72000
#-------------------------------
#cars1$car_names: Golf
# car_names saleprice
#6 Golf 22000
#-------------------------------
#cars1$car_names: Malibu
# car_names saleprice
#7 Malibu 33000
Finally, you can use also lapply
and split
(for which by
is somewhat shorthand)
lapply(split(cars1, cars1$car_names), function(x) x[which.max(x$saleprice), ])
#$Corvette
# car_names saleprice
#3 Corvette 72000
#$Golf
# car_names saleprice
#6 Golf 22000
#$Malibu
# car_names saleprice
#7 Malibu 33000
error with lapply on anonymous ggplot function
You are missing a paranthesis to close ggtitle()
as names()
also requires a closing paren.
if (inputMethodP == "WITHINFILE") {
par(mfrow = c(5, listPortions))
plotList <- lapply(RangeStatResultP, function(listPart) {
ggplot(matrixPart, aes(x = factor(Var2), y = value)) +
geom_violin()+
ggtitle(names(listPart)) +
xlab(listnum) +
ylab("Coverage") +
stat_summary(fun.y = median, geom = "point",
position = position_dodge(width = .9), size = 6, shape = 4,
show_guide = F)
})
}
Why does this lapply() function applied to a dataframe not produce the same results as its for-loop equivalent?
To make your lapply code work just replace <-
with <<-
:
DF_1[row, column] <<- DF_1[row, column] + val
Please see ?assignOps
for more info.
However, again I wouldn't recommend lapply
in this case (<<-
should be avoided in general)
Here is a data.table
approach:
library(data.table)
DT <- setDT(data.frame(
ID = c(1,1,1,2,2,2,3,3,3),
Period = c(1, 2, 3, 1, 2, 3, 1, 2, 3),
Values = c(5, 10, 15, 50, 2, 4, 3, 6, 9),
Flags = c("X1","X0","X2","X0","X2","X0", "X2","X1","X1")
))
unique_flags <- unique(DT$Flags)
all_flags <- setDT(expand.grid(list(first_flag = unique_flags, last_flag = unique_flags)))
resultDT <- dcast(
data = DT[, .(first_flag = first(Flags), last_flag = last(Flags), first_value = first(Values)), by = ID][
all_flags, on = c("first_flag", "last_flag")],
last_flag ~ first_flag,
fun.aggregate = sum,
value.var = "first_value"
)
for (col_i in seq_len(ncol(resultDT))){
set(resultDT, which(is.na(resultDT[[col_i]])), col_i, 0)
}
print(resultDT)
Result:
last_flag X0 X1 X2
1: X0 50 0 0
2: X1 0 0 3
3: X2 0 5 0
# step by step ------------------------------------------------------------
library(data.table)
DT <- setDT(data.frame(
ID = c(1,1,1,2,2,2,3,3,3,4,4,4),
Period = c(1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3),
Values = c(5, 10, 15, 50, 2, 4, 3, 6, 9, 3, 6, 9),
Flags = c("X1","X0","X2","X0","X2","X0", "X2","X1","X1", "X2","X1","X1")
))
unique_flags <- unique(DT$Flags)
all_flags <- setDT(expand.grid(list(first_flag = unique_flags, last_flag = unique_flags)))
resultDT <- DT[, .(first_flag = first(Flags), last_flag = last(Flags), first_value = first(Values)), by = ID] # find relevant flags
resultDT <- resultDT[all_flags, on = c("first_flag", "last_flag")] # merge all combinations
resultDT <- dcast(resultDT, last_flag ~ first_flag, fun.aggregate = sum, value.var = "first_value") # dcast
for (col_i in seq_len(ncol(resultDT))){
set(resultDT, which(is.na(resultDT[[col_i]])), col_i, 0)
}
print(resultDT)
lapply with anonymous function call to svytable results in object 'x' not found
Ok, well, it seems the svytable
function is picky and will only look up data in the design object. It doesn't seem to look for x
in the enclosing environment. So an alternative approach is to dynamically build the formula. So instead of passing in the columns of data themselves, we pass in names of columns form the data.frame. Then we plug those into the formula and then they are resolved by the design object which points to the original data.frame. Here's a bit of working code using your sample data
lapply(names(dat)[1:9], function(x) round(prop.table(
svytable(bquote(~.(as.name(x)) + seg_2), dat_weight),
2),3)*100)
So here we use bquote
to build the formula. The .()
allows us to plug in expressions and here we take the character value in x
and convert it to a proper name object. Thus is goes from "r3a_9"
to r3a_9
.
How to use ‘apply’ with function that returns wrong dimensions (R)
Maybe you can get some inspiration from the following example.
The anonymous function uses hard-coded function dummy
and another function, the argument fun
set equal to log
.
dummy <- function(x){
a <- max(x)
b <- min(x)
c(a, b)
}
z <- matrix(c(2,3,4,5,6,7,8,9,10), nrow = 3)
apply(z, 2, dummy)
apply(z, 2, function(x, fun){
y <- dummy(x)
y <- fun(y[2])
y
}, fun = log)
#[1] 0.6931472 1.6094379 2.0794415
Another possibility would be to have itworks
need two arguments, the vector x
and a function chosen on a case by case basis.
itworks <- function(x, fun){
qq <- quantile(x)
fun(qq[2])
}
apply(z, 2, itworks, fun = sin)
#[1] 0.5984721 -0.7055403 0.7984871
The point here is that the arguments a function takes can be anything, including other functions. apply
has 3 mandatory arguments, X
, MARGIN
and FUN
, plus the dots
argument. In the dots argument you can pass whatever you want as long as FUN
accepts it.
lapply-ing with the $ function
This is documented in ?lapply
, in the "Note" section (emphasis mine):
For historical reasons, the calls created by
lapply
are unevaluated,
and code has been written (e.g.bquote
) that relies on this. This
means that the recorded call is always of the formFUN(X[[0L]],
, with
...)0L
replaced by the current integer index. This is not
normally a problem, but it can be ifFUN
usessys.call
or
match.call
or if it is a primitive function that makes use of the
call. This means that it is often safer to call primitive functions
with a wrapper, so that e.g.lapply(ll, function(x) is.numeric(x))
is required in R 2.7.1 to ensure that method dispatch foris.numeric
occurs correctly.
Related Topics
Avoid String Printed to Console Getting Truncated (In Rstudio)
Add Max Value to a New Column in R
Why Is the Terminology of Labels and Levels in Factors So Weird
Add Number of Observations Per Group in Ggplot2 Boxplot
How to Fit a Smooth Curve to My Data in R
Error: Could Not Find Function "%>%"
Adding Greek Character to Axis Title
Calculate Cumsum() While Ignoring Na Values
What Leads the First Element of a Printed List to Be Enclosed with Backticks in R V3.5.1
Generate Dynamic R Markdown Blocks
Delete Columns/Rows with More Than X% Missing
Marker Mouse Click Event in R Leaflet for Shiny
Stacked Barplot with Colour Gradients for Each Bar