バックグラウンド
有益な議論と、SO の同僚から次の点に関して受けた支援に続いて:
便利な機能を組み合わせました。これは数値ベクトルを取り、グループに関連する因数分解されたベクトルを生成します。
関数
関数の本体を以下に示します。
nice.cuts <- function(variable, cuts = 10, thousands.separator = FALSE) {
# Load required packages (useful when used independently of context)
Vectorize(require)(package = c("gsubfn", "Hmisc", "scales"),
character.only = TRUE)
# Destring this variable
destring <- function(x) {
## convert factor to strings
if (is.character(x)) {
as.numeric(x)
} else if (is.factor(x)) {
as.numeric(levels(x))[x]
} else if (is.numeric(x)) {
x
} else {
stop("could not convert to numeric")
}
}
# Apply function
variable <- destring(variable)
# Check whether to disable scientific notation
if (mean(variable) > 100000) {
options(scipen = 999)
} else {
options(scipen = 0)
}
# Create pretty breaks
cut_breaks <- pretty_breaks(n = cuts)(variable)
# Round it two decimal places
variable <- round(variable, digits = 2)
# Develop cuts according to the provided object
cuts_variable <- cut2(x = variable, cuts = cut_breaks)
# Check if variable is total or with decimals
if (all(cut_breaks %% 1 == 0)) {
# Variable is integer
clean_cuts <- gsubfn('\\[\\s*(\\d+),\\s*(\\d+)[^0-9]+',
~paste0(x, '-',as.numeric(y)-1),
as.character(cuts_variable))
} else {
# Variable is not integer
# Create clean cuts
clean_cuts <- gsubfn('\\[\\s*([0-9]+\\.*[0-9]*),\\s*(\\d+\\.\\d+).*',
~paste0(x, '-', as.numeric(y)- 0.01),
as.character(cuts_variable))
}
# Clean Inf
clean_cuts <- gsub("Inf", max(variable), clean_cuts)
# Clean punctuation
clean_cuts <- sub("\\[(.*), (.*)\\]", "\\1 - \\2", clean_cuts)
# Replace strings with spaces
clean_cuts <- gsub("-"," - ",clean_cuts, fixed = TRUE)
# Trim white spaces
clean_cuts <- trimws(clean_cuts)
# Order factor before returning
clean_cuts <- factor(clean_cuts, levels = unique(clean_cuts[order(variable)]))
if (thousands.separator == TRUE) {
levels(clean_cuts) <- sapply(strsplit(levels(clean_cuts), " - "),
function(x) paste(prettyNum(x,
big.mark = ",",
preserve.width = "none"),
collapse = " - "))
}
# Return
return(clean_cuts)
}
結果
この関数は、マッピングに使用される係数を生成するときに非常に役立ちます。たとえば、次の値の場合:
set.seed(1)
dta <- data.frame(values=floor(runif(100, 10000,90000)))
関数はきれいな休憩を生成します
> dta$cuts <- nice.cuts(dta$values, thousands.separator = TRUE)
> t(t(table(dta$cuts))) #' t() for presentation
[,1]
10,000 - 19,999 9
20,000 - 29,999 11
30,000 - 39,999 12
40,000 - 49,999 20
50,000 - 59,999 6
60,000 - 69,999 15
70,000 - 79,999 17
80,000 - 89,999 10
驚くべき伝説を生成するために使用できるもの:
これは、コロプレス マップのデータを生成するときに非常に便利で、私は常にこれを使用しています。
問題
課題は、パフォーマンスの低下に関係しています。関数は非常に遅いようです。
非常に小さなデータセット
100 個の観測値の小さなデータ セットの場合、パフォーマンスは驚くべきものではありません。
> require(microbenchmark)
> dta <- data.frame(values=floor(runif(100, 10000,90000)))
> microbenchmark(nice.cuts(dta$values, thousands.separator = TRUE))
Unit: milliseconds
expr min lq mean median uq max neval
nice.cuts(dta$values, thousands.separator = TRUE) 32.67988 58.25709 99.26317 95.25195 136.7998 222.2178 100
小さなデータセット
わずかに大きなデータセットでも非常に遅くなります。
> dta <- data.frame(values=floor(runif(1000, 10000,90000)))
> microbenchmark(nice.cuts(dta$values, thousands.separator = TRUE),
+ times = 10)
Unit: milliseconds
expr min lq mean median uq max neval
nice.cuts(dta$values, thousands.separator = TRUE) 428.6821 901.2123 1154.097 1068.845 1679.052 1708.836 10
したがって、私の質問はかなり単純です。関数の現在の機能を維持したいのですが、nice.cuts
より高速に実行したいのです。
提案
- この
gsubfn
要素にはかなりの時間がかかると思いますが、どうすればより効率的にできるかわかりません。 - また、変数の一意の値を取得すると、少しスピードアップする可能性があると考えています。私の実際のデータでは、特定の値が繰り返されるベクトルを使用することがよくあります