DaveTurek
DaveTurek

Reputation: 1297

Normalize a vector based on two other values

I have the following dataframe

df <- data.frame(
  Type=rep(LETTERS[1:6],3),
  Level=rep(1:3,each=6),
  Value=1:18)

I'd like to add 2 columns,

to end up with

> df
   Type Level Value       r1   r2
1     A     1     1 1.000000  1.0
2     B     1     2 2.000000  1.0
3     C     1     3 3.000000  1.0
4     D     1     4 4.000000  1.0
5     E     1     5 5.000000  1.0
6     F     1     6 6.000000  1.0
7     A     2     7 1.000000  7.0
8     B     2     8 1.142857  4.0
9     C     2     9 1.285714  3.0
10    D     2    10 1.428571  2.5
11    E     2    11 1.571429  2.2
12    F     2    12 1.714286  2.0
13    A     3    13 1.000000 13.0
14    B     3    14 1.076923  7.0
15    C     3    15 1.153846  5.0
16    D     3    16 1.230769  4.0
17    E     3    17 1.307692  3.4
18    F     3    18 1.384615  3.0

I tried a few apply type approaches, but couldn't get it. I ended up with a double for loop:

for(i in unique(df$Type)) {
  for(j in unique(df$Level)) {
    df$r1[df$Level==j & df$Type==i] <- df$Value[df$Level==j & df$Type==i]/df$Value[df$Level==j & df$Type=="A"]
    df$r2[df$Level==j & df$Type==i] <- df$Value[df$Level==j & df$Type==i]/df$Value[df$Level==1 & df$Type==i]
  }
}

This isn't too bad, but I'm wondering if there is a split-apply-combine approach that would do it, maybe with something in plyr.

Upvotes: 3

Views: 358

Answers (2)

flodel
flodel

Reputation: 89057

Since you suggested a plyr solution:

df <- ddply(df, .(Level), transform, r1 = Value / Value[Type == "A"])
df <- ddply(df, .(Type),  transform, r2 = Value / Value[Level == 1])

which I think reads nicely.

Upvotes: 2

Matthew Lundberg
Matthew Lundberg

Reputation: 42659

Create vectors first, then cbind them to the data frame:

 r1 <- df$Value / df$Value[rep(df$Value[df$Type=='A'], each=length(levels(df$Type)))]
 r2 <- df$Value / df$Value[seq_along(levels(df$Type))]

This assumes that the "Types" are repeated for each "Level", as in your example.

Here is the appropriate cbind() call:

cbind(df, r1, r2)

Upvotes: 1

Related Questions