2016-08-23 8 views
0

私の仕事は、指定された変数(byvar)のレベルによって与えられたデータセット(dset)の与えられた変数(vars)の対数を計算することを目的とした関数を書くことです。所与のレベルの与えられた変数の最小値がbyvarであれば、単純な自然対数が計算されます。所与のセグメントは次のように計算されるため、さもなければ、与えられた変数の新しい値がこれを達成するためにログ変換のループ

new.value = log(old.value + 1 + abs(min.value.of.given.var.for.given.level) 

、Iは、(再現例えば)そのようなコードを書いた:

set.seed(1234567) 

data(iris) 
iris$random <- rnorm(nrow(iris), 0, 1) 

log.vars <- function(dset, vars, byvar, verbose = F){ 

    # a loop by levels of "byvar" 

    for(i in 1:length(unique(dset[[byvar]]))){ 

    if(verbose == T){ 
     print(paste0("------ level=", unique(dset[[byvar]])[i], "----")) 
    } 

    # a loop by variables in "vars" 

    for(j in 1:length(vars)){ 

     min.var <- min(dset[[vars[j]]][dset[[byvar]] == unique(dset[[byvar]])[i]]) 

     # if minimum of a given variable for a given level is greater than 0 then 
     # calculate its logarithm; 
     # otherwise, add to its value 1 and the mode of its minimum and calculate 
     # its logarithm 

     dset[[paste0("ln_", vars[j])]][dset[[byvar]] == unique(dset[[byvar]])[i]] <- 
     if(min.var > 0){ 
      log(dset[[vars[j]]][dset[[byvar]] == unique(dset[[byvar]])[i]]) 
     } else{ 
      log(dset[[vars[j]]][dset[[byvar]] == unique(dset[[byvar]])[i]] + 1 + 
       abs(min.var)) 
     } 
    } 
    } 
    return(dset) 
} 

iris2 <- log.vars(dset = iris, 
     vars = c("Sepal.Length", "random", "Sepal.Width"), 
     byvar = "Species", 
     verbose = T) 

head(iris2) 

それは動作しますしかし、読みやすさには明らかな問題があります。さらに、その性能が向上するかどうかは疑問です。最後に重要なのは、データセット内の観測の順序を保持することです。ヘルプ/提案の任意の種類の答えに私のコメントを回す

+0

これはうまくいくもので、読みやすさとパフォーマンスの向上が求められていますが、これは実際は姉妹サイトのCodeReview SEにとってより適切な質問です。 –

答えて

2

をいただければ幸いです。

車輪の再発明をしないでください。 basetapplyave)、data.tableplyr、およびdplyrに「グループで機能させる」方法があります。

my_log = function(x) { 
    m = min(x) 
    if (m > 0) return(log(x)) 
    return(log1p(x - m)) 
} 

上記のログには、説明するログが実装されています。

library(dplyr) 
iris %>% group_by(Species) %>% 
    mutate_each(funs = funs(logged = my_log)) 
# Source: local data frame [150 x 11] 
# Groups: Species [3] 
# 
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species  random Sepal.Length_logged 
#   <dbl>  <dbl>  <dbl>  <dbl> <fctr>  <dbl>    <dbl> 
# 1   5.1   3.5   1.4   0.2 setosa 0.156703769   1.629241 
# 2   4.9   3.0   1.4   0.2 setosa 1.373811191   1.589235 
# 3   4.7   3.2   1.3   0.2 setosa 0.730670244   1.547563 
# 4   4.6   3.1   1.5   0.2 setosa -1.350800927   1.526056 
# 5   5.0   3.6   1.4   0.2 setosa -0.008514961   1.609438 
# 6   5.4   3.9   1.7   0.4 setosa 0.320981863   1.686399 
# 7   4.6   3.4   1.4   0.3 setosa -1.778148409   1.526056 
# 8   5.0   3.4   1.5   0.2 setosa 0.909503835   1.609438 
# 9   4.4   2.9   1.4   0.2 setosa -0.919404336   1.481605 
# 10   4.9   3.1   1.5   0.1 setosa -0.157714831   1.589235 
# # ... with 140 more rows, and 4 more variables: Sepal.Width_logged <dbl>, Petal.Length_logged <dbl>, 
# # Petal.Width_logged <dbl>, random_logged <dbl> 

をそして、それはそれで全部です:あなたは複数の列に同じグループでこれを実行したいので、dplyr::mutate_each簡単に私たちの生活をすることができます!それは素晴らしく、簡潔で、読みやすいようです。あなたも、より多くのそれを「官能化」したい場合は、同じ結果のために、関数に以下のようなものをそれを包むことができます:あなたの3について

log_vars = function(data, vars, byvar) { 
    data %>% group_by_(byvar) %>% 
     mutate_each_(funs = funs(logged = my_log), vars = vars) %>% 
     return 
} 

log_vars(iris, vars = c("Sepal.Width", "random"), byvar = "Species") 

尋ねる:

  1. 読み取り可能これははるかに読みやすいようです。必要に応じて%>%パイプなしで書き直すことができます。
  2. パフォーマンス - これは重要な場所では、より高速になります。多数のグループを持つ大量のデータ。
  3. 順序 - 行の順序は変更されません。
+0

ありがとう、このソリューションは実行時間を改善しました! – kaksat