2015-10-20 32 views
7

Nếu tôi có một khung dữ liệu như vậy:Tốc độ lên máy tính trung bình hàng khôn ngoan của mỗi 3-tuple của cột

df = data.frame(matrix(rnorm(100), 5000, 100)) 

tôi có thể sử dụng chức năng sau đây để nhận được tất cả sự kết hợp của hàng trung vị ba hạn -wise:

median_df = t(apply(df, 1, combn, 3, median)) 

Vấn đề là, chức năng này sẽ mất vài giờ để chạy. Thủ phạm là trung bình(), mất khoảng 10 lần để chạy so với max() hoặc min().

Làm cách nào để tăng tốc chức năng này, có thể bằng cách viết phiên bản trung bình nhanh hơn() hoặc làm việc với dữ liệu gốc khác nhau?

Cập nhật:

Nếu tôi chạy đoạn mã trên nhưng chỉ cho df [, 1: 10] như vậy:

median_df = t(apply(df[,1:10], 1, combn, 3, median)) 

mất 29 giây

fastMedian_df = t(apply(df[,1:10], 1, combn, 3, fastMedian)) 

từ gói ccaPP mất 6,5 giây

max_df = t(apply(df[,1:10], 1, combn, 3, max)) 

mất 2,5 giây

Vì vậy, chúng tôi thấy sự cải thiện đáng kể với fastMedian(). Chúng ta có thể làm tốt hơn không?

+1

Trong khi 'trung vị' có thể gây ra một số vấn đề so với' max' và 'min', tôi nghĩ vấn đề thực sự với' combn'. Ví dụ, một hàng đơn ('system.time (combn (df [1,], 3))') mất ~ 10 giây trên máy tính của tôi. – nrussell

+0

@nrussell trong khi combnPrim là một thực hiện nhanh hơn nhiều combn(), tôi không thể có được combnPrim để làm việc trong trường hợp này, trả về lỗi: Lỗi trong if (simplify) {: argument không thể hiểu là logic –

+0

Trong mọi trường hợp, combn() mất ít hơn 10% thời gian trung bình() cần để chạy trong chức năng này –

Trả lời

14

Một cách tiếp cận để tăng tốc mọi thứ sẽ là lưu ý rằng trung bình của ba số là tổng của chúng trừ đi giá trị cực đại trừ phút tối thiểu của chúng. Điều này có nghĩa là chúng ta có thể vector hóa tính toán trung bình của chúng tôi bằng cách xử lý mỗi ba cột một lần (thực hiện trung vị cho tất cả các hàng trong cùng một phép tính) thay vì xử lý nó một lần cho mỗi hàng.

set.seed(144) 
# Fully random matrix 
df = matrix(rnorm(50000), 5000, 10) 
original <- function(df) t(apply(df, 1, combn, 3, median)) 
josilber <- function(df) { 
    combos <- combn(seq_len(ncol(df)), 3) 
    apply(combos, 2, function(x) rowSums(df[,x]) - pmin(df[,x[1]], df[,x[2]], df[,x[3]]) - pmax(df[,x[1]], df[,x[2]], df[,x[3]])) 
} 
system.time(res.josilber <- josilber(df)) 
# user system elapsed 
# 0.117 0.009 0.149 
system.time(res.original <- original(df)) 
# user system elapsed 
# 15.107 1.864 16.960 
all.equal(res.josilber, res.original) 
# [1] TRUE 

Vectorization mang lại tốc độ 110x khi có 10 cột và 5000 hàng. Thật không may, tôi không có một máy có đủ bộ nhớ để lưu trữ 808,5 triệu con số trong đầu ra cho ví dụ đầy đủ của bạn. Bạn có thể tăng tốc độ này bằng cách thực hiện một hàm Rcpp lấy làm đầu vào biểu diễn vector của ma trận (còn gọi là véc tơ thu được bằng cách đọc ma trận xuống các cột) cùng với số hàng và trả về giá trị trung bình của mỗi cột. Hàm dựa rất nhiều vào hàm std::nth_element, hàm này tuyến tính theo số lượng các phần tử bạn đang dùng trung bình. (Lưu ý rằng tôi không trung bình giữa hai giá trị khi tôi lấy trung bình của một vectơ có độ dài bằng nhau; thay vào đó tôi lấy giá trị trung bình của hai giá trị này).

library(Rcpp) 
cppFunction(
"NumericVector vectorizedMedian(NumericVector x, int chunkSize) { 
const int n = x.size()/chunkSize; 
std::vector<double> input = Rcpp::as<std::vector<double> >(x); 
    NumericVector res(n); 
    for (int i=0; i < n; ++i) { 
    std::nth_element(input.begin()+i*chunkSize, input.begin()+i*chunkSize+chunkSize/2, 
        input.begin()+(i+1)*chunkSize); 
    res[i] = input[i*chunkSize+chunkSize/2]; 
    } 
    return res; 
}") 

Bây giờ chúng ta chỉ cần gọi chức năng này thay vì sử dụng rowSums, pminpmax:

josilber.rcpp <- function(df) { 
    combos <- combn(seq_len(ncol(df)), 3) 
    apply(combos, 2, function(x) vectorizedMedian(as.vector(t(df[,x])), 3)) 
} 
system.time(josilber.rcpp(df)) 
# user system elapsed 
# 0.049 0.008 0.081 
all.equal(josilber(df), josilber.rcpp(df)) 
# [1] TRUE 

Trong tổng số do đó chúng tôi có được một tăng tốc 210x; 110x của quá trình tăng tốc là từ việc chuyển đổi từ ứng dụng không được vector hóa là median sang ứng dụng được vector hóa và tăng tốc 2x còn lại là từ việc chuyển đổi từ sự kết hợp của rowSums, pminpmax để tính trung bình theo cách được vector hóa theo Rcpp tiếp cận.

+0

Liệu nó có hợp lý để vector hóa trong chiều không gian khác? Sẽ có 161700 kết hợp 3 cho 100 cột, nhưng chỉ có 5000 hàng dữ liệu. –

+0

@MartinMorgan Tôi không ngay lập tức thấy bạn sẽ làm như thế nào, nhưng bạn chắc chắn rằng đầu ra là rộng hơn nó dài. – josliber

+1

't (áp dụng (df, 1, hàm (y) vectorizedMedian (y [combo], 3)))' nhưng cuối cùng nó dường như không tạo ra nhiều khác biệt. –

Các vấn đề liên quan