2012-04-09 9 views
21

Tôi có một dataframe và tôi muốn áp dụng hàm có giá trị của ba cột và tính toán sự khác biệt tối thiểu giữa ba giá trị.sử dụng nhiều cột dưới dạng biến với sapply

#dataset 
df <- data.frame(a= sample(1:100, 10),b = sample(1:100, 10),c= sample(1:100, 10)) 

#function 
minimum_distance <- function(a,b,c) 
{ 
    dist1 <- abs(a-b) 
    dist2 <- abs(a-c) 
    dist3 <- abs(b-c) 
    return(min(dist1,dist2,dist3)) 
} 

Tôi đang tìm kiếm một cái gì đó như:

df$distance <- sapply(df, function(x) minimum_distance(x$a,x$b,x$c)) 
## errormessage 
Error in x$a : $ operator is invalid for atomic vectors 

Trong khi tôi có thể sử dụng ddply:

df2 <- ddply(df,.(a),function(r) {data.frame(min_distance=minimum_distance(r$a,r$b, r$c))}, .drop=FALSE) 

này không giữ tất cả các cột. Bất kỳ đề xuất?

Edit: tôi đã kết thúc bằng:

df$distance <- mapply(minimum_distance, df$a, df$b, df$c) 

Trả lời

38

Hãy thử mapply():

qq <- mapply(minimum_distance, df$a, df$b, df$c) 
+0

đơn giản và thanh lịch. thanks – zach

+0

Ine nào là nhanh nhất? hoặc hiệu quả hơn? – Bharath

6

thử điều này:

do.call("mapply", c(list(minimum_distance), df)) 

nhưng bạn có thể viết phiên bản vectorized:

pminimum_distance <- function(a,b,c) 
{ 
dist1 <- abs(a-b) 
dist2 <- abs(a-c) 
dist3 <- abs(b-c) 
return(pmin(dist1,dist2,dist3)) 
} 
pminimum_distance(df$a, df$b, df$c) 

# or 
do.call("pminimum_distance", df) 
+0

điều này là thông minh nhưng một chút ít đơn giản cảm ơn một cách tinh tế. – zach

4

Tôi biết điều này đã được trả lời nhưng tôi thực sự muốn tham gia một cách tiếp cận khác nhau mà phải mất bất kỳ số lượng cột và là khái quát hóa hơn sử dụng một cách tiếp cận bên ngoài:

vdiff <- function(x){ 
    y <- outer(x, x, "-") 
    min(abs(y[lower.tri(y)])) 
} 

apply(df, 1, vdiff) 

Tôi nghĩ rằng đây là một trình dọn dẹp chút và linh hoạt.

EDIT: Mỗi nhận xét của zach Tôi đề xuất hàm được chính thức hóa hơn này hoạt động trên các khung dữ liệu có cột số cũng bằng cách xóa chúng và chỉ hoạt động trên các cột số.

cdif <- function(dataframe){ 
    df <- dataframe[, sapply(dataframe, is.numeric)] 
    vdiff <- function(x){ 
     y <- outer(x, x, "-") 
     min(abs(y[lower.tri(y)])) 
    } 
    return(apply(df, 1, vdiff)) 
} 

#TEST it out 
set.seed(10) 
(df <- data.frame(a = sample(1:100, 10), b = sample(1:100, 10), 
    c = sample(1:100, 10), d = LETTERS[1:10])) 

cdif(df) 
+0

ý tưởng hay. dataframe thực sự của tôi không phải là một ma trận Tuy nhiên - điều này có thể được sửa đổi để sử dụng trong một dataframe với các cột văn bản? một cái gì đó như bên ngoài (x, x, "-", drop_string = T)? – zach

+0

Hàm 'outer' không nhất thiết có nghĩa là bạn đang làm việc trên một ma trận. Nó chỉ mất hai vectơ và một hàm và tạo ra một ma trận của tất cả các kết hợp có thể cho hai vectơ đó. Ở đây tôi chỉ cung cấp cùng một vectơ (hàng) cho bên ngoài hai lần và toán tử '-' phép trừ. Tôi đã thêm một chút vào giải pháp của mình để tạo ra một hàm tự chứa hoạt động trên các khung dữ liệu và loại trừ bất kỳ thứ gì không phải là số. 'bên ngoài 'có thể rất mạnh mẽ Tôi chỉ ước mình có thể nhớ sử dụng nó nhiều hơn. Theo như drop_string = T? Không may mắn như vậy nhưng 'sapply' với một truy vấn' is.numeric' hoạt động tốt. –

+0

rất đẹp. Tôi đồng ý rằng bên ngoài là khá mạnh mẽ và cho một ma trận lớn hơn này sẽ là con đường để đi chứ không chỉ định mỗi cột hoặc giá trị. – zach

0

của nó tốt hơn để viết một hàm và sau đó sử dụng mapply trên các vectơ:

f1 <- function(a,b,c){ 
d =abs(a-b) 
e =abs(b-c) 
f= abs(c-a) 
return(pmin(d,e,f)) 
} 

qq <- mapply(f1, df$a, df$b, df$c)