2016-08-26 14 views
5

Mam dataframe:Dataframe utworzyć nową kolumnę na podstawie innych kolumn

df <- data.frame('a'=c(1,2,3,4,5), 'b'=c(1,20,3,4,50)) 
df 
    a b 
1 1 1 
2 2 20 
3 3 3 
4 4 4 
5 5 50 

i chcę utworzyć nową kolumnę na podstawie istniejących kolumn. Coś takiego:

if (df[['a']] == df[['b']]) { 
    df[['c']] <- df[['a']] + df[['b']] 
} else { 
    df[['c']] <- df[['b']] - df[['a']] 
} 

Problem polega na tym, że warunek if sprawdzana jest tylko dla pierwszego rzędu ... Jeśli utworzyć funkcję z powyższym stwierdzeniem if następnie używam apply() (lub mapply() ...) to jest to samo.

W Pythonie/pandy mogę użyć tego:

df['c'] = df[['a', 'b']].apply(lambda x: x['a'] + x['b'] if (x['a'] == x['b']) \ 
    else x['b'] - x['a'], axis=1) 

Chcę coś podobnego w R. Tak więc wynik powinien wyglądać następująco:

a b c 
1 1 1 2 
2 2 20 18 
3 3 3 6 
4 4 4 8 
5 5 50 45 
+0

Problem polega na tym, że gdy używa się == jako operatora logicznego, który rzeczywiście tylko jeden, respekt ely wybrano pierwszy wpis. Wektoryzowana odpowiedź @akrun powinna wykonać zadanie. – jd1338

+3

Technicznie, możesz też użyć czegoś takiego jak 'with (df, (a * c (-1L, 1L) [(a == b) + 1L]) + b)' ale to nie jest bardzo intuicyjne –

Odpowiedz

14

Jedną z opcji jest ifelse który jest wersja vectorized z if/else. Jeśli robimy to dla każdego wiersza, to if/else, jak pokazano w postach Panda OP można zrobić w pętli for lub lapply/sapply, ale byłoby to nieefektywne w R.

df <- transform(df, c= ifelse(a==b, a+b, b-a)) 
df 
# a b c 
#1 1 1 2 
#2 2 20 18 
#3 3 3 6 
#4 4 4 8 
#5 5 50 45 

Można to inaczej zapisać jako

df$c <- with(df, ifelse(a==b, a+b, b-a)) 

stworzyć 'c' kolumny w oryginalnym zbiorze


Jak PO chce podobną opcję w R używanie if/else

df$c <- apply(df, 1, FUN = function(x) if(x[1]==x[2]) x[1]+x[2] else x[2]-x[1]) 
+0

Dziękuję! Czy mógłbyś podać również 'apply()' (lub 'sapply()', 'mapply()', 'tapply()', 'lapply()'), jeśli to możliwe (lub link do podstawowych przykładów)?Chcę zrozumieć ich mechanizm za pomocą tego prostego przykładu (muszę "zastosować" bardziej złożone funkcje i warunki). Wielkie dzięki!! – ragesz

+1

@ragesz Jeśli chcesz zrozumieć, gdzie używać tych funkcji, [to] (http://stackoverflow.com/questions/3505701/r-grouping-functions-sapply-vs-lapply-vs-apply-vs-tapply- vs-by-vs-aggrega) może ci pomóc. – akrun

+2

@ragesz Gdy dostępne są rozwiązania wektorowe, użycie powolnych pętli 'apply()' jest złym pomysłem. Nie należy próbować używać [określonego typu polecenia do rozwiązania problemu] (http://meta.stackexchange.com/a/66378). Zamiast tego ważne jest, aby dowiedzieć się, które metody są odpowiednie w jakich przypadkach. Wektoryzowane rozwiązania w tej odpowiedzi pokazują poprawny sposób rozwiązania problemu w R. – RHertel

7

Tu jest nieco bardziej skomplikowane metody algebraiczne:

df$c <- with(df, b + ((-1)^((a==b)+1) * a)) 

df 
    a b c 
1 1 1 2 
2 2 20 18 
3 3 3 6 
4 4 4 8 
5 5 50 45 

Chodzi o to, że operator „minus” jest włączone lub wyłączone na podstawie testu a==b.

+0

To bardzo miłe, dziękuję! W rzeczywistości chodziło mi o "stworzenie nowej kolumny opartej na istniejących" i właśnie stworzyłem prosty podstawowy przykład, aby zademonstrować ten problem. Ale twoje rozwiązanie jest bardzo intuicyjne, mogę zrozumieć R nieco więcej (jak R przelicza wartość logiczną na całkowitą automatycznie itd.). – ragesz

4

Rozwiązanie z apply

myFunction <- function(x){ 
    a <- x[1] 
    b <- x[2] 
    #further values ignored (if there are more than 2 columns) 
    value <- if(a==b) a + b else b - a 
    #or more complicated stuff 
    return(value) 
} 

df$c <- apply(df, 1, myFunction) 
4

Jeśli chcesz metodę stosuje się, po czym inny sposób z mapply byłoby utworzyć funkcję i zastosowanie go,

fun1 <- function(x, y) if (x == y) {x + y} else {y-x} 
df$c <- mapply(fun1, df$a, df$b) 
df 
# a b c 
#1 1 1 2 
#2 2 20 18 
#3 3 3 6 
#4 4 4 8 
#5 5 50 45 
3

Korzystanie dplyr pakiet:

library(dplyr) 

df <- df %>% 
    mutate(c = if_else(a == b, a + b, b - a)) 

df 
# a b c 
# 1 1 1 2 
# 2 2 20 18 
# 3 3 3 6 
# 4 4 4 8 
# 5 5 50 45 
+0

Czy możesz podać informacje na temat wydajności w porównaniu z, powiedzmy, odpowiedzią @ akrun? –

+0

@hello_there_andy możesz to przetestować i edytować post. – zx8754

+0

może pewnego dnia –

Powiązane problemy