Reputation: 107
I'm trying to write a program that takes a large data frame and replaces each column of values by the cumulative frequency of those values (sorted ascending). For instance, if the column of values are: 5, 8, 3, 5, 4, 3, 8, 5, 5, 1. Then the relative and cumulative frequencies are:
Then the original column becomes: 0.8, 1.0, 0.3, 0.8, 0.4, 0.3, 1.0, 0.8, 0.8, 0.1
The following code performs this operation correctly, but it scales poorly probably due to the nested loop. Any idea how to perform this task more efficiently?
mydata = read.table(.....)
totalcols = ncol(mydata)
totalrows = nrow(mydata)
for (i in 1:totalcols) {
freqtable = data.frame(table(mydata[,i])/totalrows) # create freq table
freqtable$CumSum = cumsum(freqtable$Freq) # calc cumulative freq
hashtable = new.env(hash=TRUE)
nrows = nrow(freqtable)
# store cum freq in hash
for (x in 1:nrows) {
dummy = toString(freqtable$Var1[x])
hashtable[[dummy]] = freqtable$CumSum[x]
}
# replace original data with cum freq
for (j in 1:totalrows) {
dummy = toString(mydata[j,i])
mydata[j,i] = hashtable[[dummy]]
}
}
Upvotes: 5
Views: 1339
Reputation: 174908
Here is one way. Using a data frame with two variables each containing your example data
d <- data.frame(var1 = c(5, 8, 3, 5, 4, 3, 8, 5, 5, 1),
var2 = c(5, 8, 3, 5, 4, 3, 8, 5, 5, 1))
use a simple function to
cumsum()
of the relative proportions given by table(x) / length(x)
, thenmatch()
the observations in a variable with the names of the table of cumulative sums, thenSuch a functions is:
f <- function(x) {
tab <- cumsum(table(x) / length(x))
ind <- match(x, as.numeric(names(tab)))
unname(tab[ind])
}
In practice we use lapply()
and coerce to a data frame:
out <- data.frame(lapply(d, f))
out
which gives:
R> out
var1 var2
1 0.8 0.8
2 1.0 1.0
3 0.3 0.3
4 0.8 0.8
5 0.4 0.4
6 0.3 0.3
7 1.0 1.0
8 0.8 0.8
9 0.8 0.8
10 0.1 0.1
Upvotes: 1
Reputation: 68849
This handles a single column without the for
-loop:
R> x <- c(5, 8, 3, 5, 4, 3, 8, 5, 5, 1)
R> y <- cumsum(table(x)/length(x))
R> y[as.character(x)]
5 8 3 5 4 3 8 5 5 1
0.8 1.0 0.3 0.8 0.4 0.3 1.0 0.8 0.8 0.1
Upvotes: 2