简体   繁体   中英

R: comparing 1 data frame with two other data frames of different lengths

I have 3 data frames of unknown length.

Data frame A looks like this:

    A1  A2   n
1    1   2   1
2    3   2   2
3    2   4   3

In a similar manner, data frame B looks like this:

    B1  B2   n
1    3   4   1
2    4   1   2
3    1   3   3

Note that for every row A1, A2, B1, B3 are all different and comprise the numbers from 1 to 4.

Finally, I have data frame C :

    n C1
1   1  3
2   1  1
3   1  4
4   2  0
5   2  2
6   2  3
7   3  3
8   3  0
9   3  1

Note that the values of C1 are all between 0 and 4.

The n column connects all the data frames. What I want to do is check if the values of C1 are located in the A dataframe or the B , and this for each n . And replace it diretcly in C1. If if the value is 0 it should remain 0. This is the result I'm expecting:

    n C1
1   1  B
2   1  A
3   1  B
4   2  0
5   2  A
6   2  A
7   3  B
8   3  0
9   3  B

How can I accomplish this? Thank you for your inputs.

Here is one idea. We first merge the first two data frames. Once we merge , we can now create a new data frame by stack ing all columns (except n ). By creating this new data frame ( df5 in our case) we can now match the pasted n - value from df5 with the pasted n - C1 from your third data frame ( df4 in our case). A simple gsub operation then extracts just the letters from the matched values. As a last step, we set the NAs with 0.

df_all <- merge(df2, df3, by = 'n')
#  n A1 A2 B1 B2
#1 1  1  2  3  4
#2 2  3  2  4  1
#3 3  2  4  1  3

df5 <- data.frame(n = 1:nrow(df_all), stack(df_all[-1]), stringsAsFactors = FALSE)
#head(df5)
#  n values ind
#1 1      1  A1
#2 2      3  A1
#3 3      2  A1
#4 1      2  A2
#5 2      2  A2
#6 3      4  A2
ind <- gsub('\\d+', '', df5$ind)[match(do.call(paste, df4), do.call(paste, df5[-3]))]
ind[is.na(ind)] <- 0
ind
#[1] "B" "A" "B" "0" "A" "A" "B" "0" "B"

Another, slightly different, approach is to left outer join both A and B to C first, then find the column added by the joins that is equal to C1 :

## Do the left outer joins with merge by n and all.x=TRUE
out <- merge(merge(C,A,by="n",all.x=TRUE),B,by="n",all.x=TRUE)
## Loop over rows and extract the name of the column whose value matches C1
## first define a function to do so
extract.name <- function(i,out) {
  j <- which(out$C1[i]==out[i,3:ncol(out)])
  if (length(j)==0) return("0") else return(substr(colnames(out)[j[1]+2],1,1))                       
}
## Then, apply it to all rows
out$C1 <- sapply(1:nrow(out),extract.name,out)
## Keep only the n and C1 columns as output
out <- out[,1:2]
##  n C1
##1 1  B
##2 1  A
##3 1  B
##4 2  0
##5 2  A
##6 2  A
##7 3  B
##8 3  0
##9 3  B

Data:

A <- structure(list(A1 = c(1L, 3L, 2L), A2 = c(2L, 2L, 4L), n = 1:3), .Names = c("A1", 
"A2", "n"), class = "data.frame", row.names = c("1", "2", "3"
))
##  A1 A2 n
##1  1  2 1
##2  3  2 2
##3  2  4 3

B <- structure(list(B1 = c(3L, 4L, 1L), B2 = c(4L, 1L, 3L), n = 1:3), .Names = c("B1", 
"B2", "n"), class = "data.frame", row.names = c("1", "2", "3"
))
##  B1 B2 n
##1  3  4 1
##2  4  1 2
##3  1  3 3

C <- structure(list(n = c(1L, 1L, 1L, 2L, 2L, 2L, 3L, 3L, 3L), C1 = c(3L, 
1L, 4L, 0L, 2L, 3L, 3L, 0L, 1L)), .Names = c("n", "C1"), class = "data.frame", row.names = c("1", 
"2", "3", "4", "5", "6", "7", "8", "9"))
##  n C1
##1 1  3
##2 1  1
##3 1  4
##4 2  0
##5 2  2
##6 2  3
##7 3  3
##8 3  0
##9 3  1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM