5
votes

I've been looping over values in a dplyr tbl_df, trying to print unique combinations of two columns. After much trial and error I've only been able to get exactly the desired output by converting the tbl_df back to a standard data.frame. I'm aware of the main differences between the two structures but I still cant understand the differing output I'm seeing with each.

For example, using this data

hospital <- rep(c("Hospital 1", "Hospital 2", "Hospital 3"), 3)
ward <- LETTERS[1:2]
hospitals <- data.frame(cbind(hospital, ward))
hospitals[order(hospitals$hospital, hospitals$ward), ]

#     hospital ward
# 1 Hospital 1    A
# 7 Hospital 1    A
# 4 Hospital 1    B
# 5 Hospital 2    A
# 2 Hospital 2    B
# 8 Hospital 2    B
# 3 Hospital 3    A
# 9 Hospital 3    A
# 6 Hospital 3    B

and the following loop

for(hosp in unique(hospitals$hospital)){
  for(wa in unique(hospitals[hospitals$hospital==hosp, "ward"])){
    print(paste(hosp, wa, sep=" "))
    }
  }

I can get my desired output

#[1] "Hospital 1 A"
#[1] "Hospital 1 B"
#[1] "Hospital 2 B"
#[1] "Hospital 2 A"
#[1] "Hospital 3 A"
#[1] "Hospital 3 B"

But using a tbl_df of the same data I get a different output

hospitals2 <- tbl_df(hospitals)

for(hosp in unique(hospitals2$hospital)){
  for(wa in unique(hospitals2[hospitals2$hospital==hosp, "ward"])){
    print(paste(hosp, wa, sep=" "))
    }
  }


#[1] "Hospital 1 A" "Hospital 1 B"
#[1] "Hospital 2 B" "Hospital 2 A"
#[1] "Hospital 3 A" "Hospital 3 B"

It's not just a printing difference, this appears to be three two-element vectors instead of six one-element vectors, and my subsequent code only works as expected when I run the loop on a normal dataframe.

Can anyone explain why I'm seeing these differences?

1

1 Answers

6
votes

You can't do for loop on tbl_df with subsetting[. Documentation says it all :

[ Never simplifies (drops), so always returns data.frame.

You see that hospitals2[hospitals2$hospital==hosp, "ward"] returns data.frame

hospitals2[hospitals2$hospital==hosp, "ward"]
#Source: local data frame [3 x 1]

#  ward
#1    A
#2    B
#3    A

whereas

hospitals[hospitals$hospital==hosp, "ward"]
#[1] A B A
#Levels: A B

Use [[ to extract a column vector, for instance

for(hosp in unique(hospitals2$hospital)){
    for(wa in unique(hospitals[hospitals$hospital==hosp,][["ward"]])){
        print(paste(hosp, wa, sep=" "))
    }
} 
#[1] "Hospital 1 A"
#[1] "Hospital 1 B"
#[1] "Hospital 2 B"
#[1] "Hospital 2 A"
#[1] "Hospital 3 A"
#[1] "Hospital 3 B"