David H.
David H.

Reputation: 31

Syntax of for loops in R

I'm coming over to R from Stata and have a simple syntax question that I haven't been able to solve. I'm trying to loop across multiple currencies (GBP EUR JPY) to make an exchange rate adjustment. For each record denominated in one of the currencies listed above, set "amount_usd" equal to "amount" divided by the exchange rate (USDGBP, USDEUR, USDJPY).

Create data:

df <- data.frame(currency = c("GBP", "GBP", "EUR", "EUR", "JPY"), 
                 amount = c(100, 200, 100, 200, 100), 
                 USDGBP = c(1.5, 1.5, 1.5, 1.5, 1.5), 
                 USDEUR = c(1.1, 1.1, 1.1, 1.1, 1.1), 
                 USDJPY = c(100, 100, 100, 100, 100))

df$amount_usd <- df$amount

I tried various versions of the for loop below, but without any luck. I'd appreciate pointers on (1) proper loop syntax, and (2) tips for making the code more elegant.

df$face_value_curr_usd <- df$face_value_curr

for (curr in c("GBP", "EUR", "JPY")) {
    if (df[,which(colnames(df) == "currency")] == curr) {
        df[,which(colnames(df) == "amount_usd")] <-
            df[, which(colnames(df) == "amount")] / df[,which(colnames(df) == c("USD",curr))]
    }
    else {
    }
}

The resultant df$amount_usd should take on the values:

c(66.67, 133.33, 90.91, 181.82, 1)

Upvotes: 3

Views: 140

Answers (2)

Brian Stamper
Brian Stamper

Reputation: 2263

What you really want are vectorized operations instead of loops. The idea is to subset the part of the data you want to modify using row indexing, and then do all of the computing at once on that.

First of all, you don't need to reference columns numerically. Instead of

df[,which(colnames(df) == "currency")]

you can just use

df[, "currency"]

or it's alias with the $ operator

df$currency

You can use a vector to identify which rows of your data frame match a given value

gbp_rows <- df$currency == "GBP"

and you can use that to assign new values to only those rows, something like

df$face_value_curr_usd[gbp_rows] <- df$amount[gbp_rows] / df$fxGBP[gbp_rows]

Although I have to think that fxGBP is a constant, correct? So maybe something like this will do?

df$face_value_curr_usd[gbp_rows] <- df$amount[gbp_rows] / fxGBP

As has been commented, a reproducible version of your problem would be helpful, I'm not sure if this is what you are trying to do.

EDIT: A combination of what I said and Cris's answer now that we have a df:

 df <- data.frame(currency = c("GBP", "GBP", "EUR", "EUR", "JPY"), 
                 amount = c(100, 200, 100, 200, 100), 
                 USDGBP = c(1.5, 1.5, 1.5, 1.5, 1.5), 
                 USDEUR = c(1.1, 1.1, 1.1, 1.1, 1.1), 
                 USDJPY = c(100, 100, 100, 100, 100))

for(curr in unique(df$currency)) {
  this_curr_rows <- df$currency == curr
  df$amount_usd[this_curr_rows] <- df$amount[this_curr_rows] / df[this_curr_rows, paste0("USD", curr)]
}

Upvotes: 4

Cris
Cris

Reputation: 796

Using this data:

df <- data.frame(currency = c("GBP", "GBP", "EUR", "EUR", "JPY"), 
                 amount = c(100, 200, 100, 200, 100), 
                 USDGBP = c(1.5, 1.5, 1.5, 1.5, 1.5), 
                 USDEUR = c(1.1, 1.1, 1.1, 1.1, 1.1), 
                 USDJPY = c(100, 100, 100, 100, 100))

You can do:

for(curr in unique(df$currency)){
  df[paste("amount_usd",curr,sep="-")]=df$amount/df[paste("USD",curr, sep="")]
}

Which produce this results:

currency    amount  USDGBP  USDEUR  USDJPY  amount_usd-GBP  amount_usd-EUR  amount_usd-JPY
GBP 100 1.5 1.1 100 66.7    90.9    1
GBP 200 1.5 1.1 100 133.3   181.8   2
EUR 100 1.5 1.1 100 66.7    90.9    1
EUR 200 1.5 1.1 100 133.3   181.8   2
JPY 100 1.5 1.1 100 66.7    90.9    1

So its gives you the exchange rate adjustment for all your currencies. Hope it helps.

Upvotes: 3

Related Questions