2017-06-19 6 views
0

Ich habe 2 Dateien.R: benutzerdefinierte Funktionsproblem mit anwenden

"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 

Ich versuche, ein Zuwachs zu Spalte 2 der 'input.tab' anzuwenden, wie zum Beispiel folgende:

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 
... 

, um diese Ausgabe zu erhalten:

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 

Mein Plan ist apply zu verwenden, um die Eingabedatei Zeile für Zeile zu verarbeiten:

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)) 

Aber ich bekomme diese Fehlermeldung, die ich nicht wirklich verstehen kann.

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 

Warum 'my_fun' wird nicht als eine Funktion betrachtet?

Antwort

2

Ihr Anruf zu apply ist fehlgeschlagen, da Ihr drittes Argument das Ergebnis eines Funktionsaufrufs ist, keine Funktion selbst. Darüber hinaus wird es fehlschlagen, da es mit Ihren rudimentären Daten funktioniert, wenn in Ihrem data.frame andere Datentypen vorhanden sind, da apply das data.frame in matrix konvertiert, was zu Typumwandlungen führen wird. Aus diesem Grund (und ein paar anderen Gründen) empfehle ich hier apply hier zu verwenden.

Ich denke, Sie können es ziemlich leicht vektorisieren, und der Trick, in die grp -basierte Ergänzungen zu bringen, kann mit merge gelöst werden. (Es kann auch mit dplyr::left_join erfolgen.)

Ihre Daten:

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) 

Ich werde diese aktualisieren, damit die Anpassungen auf der $increment Spalte basieren. Sie können ersetzen$increment anstelle von hinzufügen$add, zu Ihnen.

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 

Von hier ist es einfach eine Anpassung.Beide sind

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 

(Ich habe ein bisschen ausführlicher gewesen mit Spalten und so. Dies kann sicherlich ein wenig kompakter gemacht werden, aber ich wollte es Raum sein, zu verstehen, was getan wird und wo.)

1

Gehen Sie dazu wie folgt vor: case_when von dplyr. Ich habe Ihre increment.tab nicht verwendet, da die Zahlen nicht mit Ihrem Beispiel übereinstimmen.

dplyr Version 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 Version 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)) 

Daten

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) 
+0

Danke P Lapointe! Funktioniert gut mit dplyr – user31888

1

zuerst einen Vektor erstellen, um pos Werte von

vec = setNames(object = c(0, 10, 35, 70), nm = c(1, 2, 3, 4)) 
vec 
# 1 2 3 4 
# 0 10 35 70 

Dann Nachschlag die entsprechenden Werte aus vec und fügen Sie zum nachschlagen. Mit P Daten 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 
+1

Ich denke, ich bevorzuge diese Verwendung von 'match' als Nachschlag, wie es Ihnen ermöglicht,' nomatch = -Inf' (zum Beispiel) für, wenn die 'grp' Lookup fehlschlägt. Meine "Merge" -Antwort würde "NA" ergeben, mit zusätzlichem Aufwand für die Reparatur/Änderung. – r2evans

+1

Danke d.b! Funktioniert super – user31888

1

Sie sind in der Nähe, aber wie @ r2evans erklärte Ihr Funktionsaufruf ist problematisch, und apply verwendet Matrizen. Ihre Lösung ist eine gute, aber für den Fall, dass Sie immer noch Ihre Funktion verwenden möchten, müssen Sie nur die Anwendung leicht ändern und adply aus der Bibliothek plyr verwenden. Mit Ihrem Beispiel ref und input Datenrahmen, wie oben, und ohne Ihre Funktion selbst überhaupt zu verändern:

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 

Wenn Sie mit apply halten wollen, sind, können Sie diesen Weg gehen (auch hier ohne Ihre Funktion zu ändern):

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 
+0

Dank Luke C für die Erklärung und für die Aufrechterhaltung meiner Funktion. Ich verstehe jetzt meinen Fehler. – user31888

Verwandte Themen