我有2个文件。
" increment.tab"
grp increment
1 10
2 25
3 35
4 50
" input.tab"
grp pos
1 10
1 14
1 25
2 3
2 20
3 2
3 10
我正在尝试将增量应用于' input.tab'的第2列。如下例如:
if grp=1, then increment=0
if grp=2, then increment=10
if grp=3, then increment=10+25=35
if grp=4, then increment=10+25+35=70
...
以获得此输出:
grp pos pos_adj
1 10 10
1 14 14
1 25 25
2 3 13
2 20 30
3 2 37
3 10 45
我的计划是使用apply
逐行处理输入文件:
ref <- read.table("increment.tab", header=T, sep="\t")
input <- read.table("input.tab", header=T, sep="\t")
my_fun <- function(x, y){
if(x==1){
inc=0
}
else{
inc=sum(ref[1:match(x, ref$grp)-1,2])
}
result = y + inc
return(result)
}
input$pos_adj = apply(input, 1, my_fun(input$grp, input$pos))
但是我收到了这个我无法理解的错误信息。
Error in match.fun(FUN) :
'my_fun(input$grp, input$pos)' is not a function, character or symbol
In addition: Warning message:
In if (x == 1) { :
the condition has length > 1 and only the first element will be used
为什么&#39; my_fun&#39;不被视为功能?
答案 0 :(得分:2)
您对apply
的调用失败,因为它的第三个参数是函数调用的结果,而不是函数本身。此外,虽然它可以根据您的基本数据工作,但如果您的data.frame中有任何其他数据类型,它将失败,因为apply
将data.frame转换为matrix
,这将导致在类型转换中。正因为这个(以及其他一些原因)我建议不要在这里使用apply
。
我认为您可以相当容易地对其进行矢量化,并且可以使用grp
解决引入基于merge
的添加的技巧。 (也可以使用dplyr::left_join
完成。)
您的数据:
increment <- read.table(text = "grp increment
1 10
2 25
3 35
4 50", header = TRUE)
input <- read.table(text = "grp pos
1 10
1 14
1 25
2 3
2 20
3 2
3 10", header = TRUE)
我会更新此内容,以便调整基于$increment
列。您可以替换 $increment
而不是添加 $add
给您。
increment$add <- c(0, cumsum(increment$increment[-nrow(increment)]))
increment
# grp increment add
# 1 1 10 0
# 2 2 25 10
# 3 3 35 35
# 4 4 50 70
x <- merge(input, increment[,c("grp", "add")], by = "grp")
x
# grp pos add
# 1 1 10 0
# 2 1 14 0
# 3 1 25 0
# 4 2 3 10
# 5 2 20 10
# 6 3 2 35
# 7 3 10 35
从这里开始,这只是一个调整问题。这两个都是
x$pos_adj <- x$pos + x$add
x$add <- NULL # remove the now-unnecessary column
x
# grp pos pos_adj
# 1 1 10 10
# 2 1 14 14
# 3 1 25 25
# 4 2 3 13
# 5 2 20 30
# 6 3 2 37
# 7 3 10 45
(我对列等等有点冗长。这肯定会变得更紧凑,但我希望有一个空间来理解正在做什么以及在哪里。)
答案 1 :(得分:1)
以下是使用case_when
中dplyr
的方法。我没有使用你的increment.tab,因为这些数字与你的例子不符。
dplyr版本0.5.0
library(dplyr)
input.tab%>%
mutate(pos_adj=case_when(.$grp==1 ~ .$pos+0,
.$grp==2 ~ .$pos+10,
.$grp==3 ~ .$pos+35,
.$grp==4 ~ .$pos+70))
grp pos pos_adj
1 1 10 10
2 1 14 14
3 1 25 25
4 2 3 13
5 2 20 30
6 3 2 37
7 3 10 45
dplyr版本0.7.0
library(dplyr)
input.tab%>%
mutate(pos_adj=case_when(grp==1 ~ pos+0,
grp==2 ~ pos+10,
grp==3 ~ pos+35,
grp==4 ~ pos+70))
数据强>
input.tab <- read.table(text="grp pos
1 10
1 14
1 25
2 3
2 20
3 2
3 10",header=TRUE,stringsAsFactors=FALSE)
答案 2 :(得分:1)
首先创建一个向量来查找
中的值vec = setNames(object = c(0, 10, 35, 70), nm = c(1, 2, 3, 4))
vec
# 1 2 3 4
# 0 10 35 70
然后,从vec
查找相应的值并添加到pos
。使用 P Lapointe 数据
increment.tab$pos + vec[match(increment.tab$grp, names(vec))]
# 1 1 1 2 2 3 3
#10 14 25 13 30 37 45
答案 3 :(得分:1)
你很接近,但是@ r2evans解释说你的函数调用有问题,apply
使用矩阵。他们的解决方案很好,但是如果您仍想使用您的函数,您只需稍微修改其应用程序并使用adply
库中的plyr
。使用上述示例ref
和input
数据框,而不更改您的功能本身:
new_df <- adply(input, 1, function(df){
c(pos_adj = my_fun(df$grp, df$pos))
})
> new_df
grp pos pos_adj
1 1 10 10
2 1 14 14
3 1 25 25
4 2 3 13
5 2 20 30
6 3 2 37
7 3 10 45
如果你想坚持apply
,你可以走这条路(再次,不改变你的功能):
input$pos_adj <- apply(input, 1, function(df){
my_fun(df["grp"], df["pos"])
})
> input
grp pos pos_adj
1 1 10 10
2 1 14 14
3 1 25 25
4 2 3 13
5 2 20 30
6 3 2 37
7 3 10 45