Comparing two columns: logical- is value from column 1 also in column 2?

泪湿孤枕 提交于 2019-12-24 09:32:37

问题


I'm pretty confused on how to go about this. Say I have two columns in a dataframe. One column a numerical series in order (x), the other specifying some value from the first, or -1 (y). These are results from a matching experiment, where the goal is to see if multiple photos are taken of the same individual. In the example below, there 10 photos, but 6 are unique individuals. In the y column, the corresponding x is reported if there is a match. y is -1 for no match (might as well be NAs). If there is more than 2 photos per individual, the match # will be the most recent record (photo 1, 5 and 7 are the same individual below). The group is the time period the photo was take (no matches within a group!). Hopefully I've got this example right:

x <- c(1,2,3,4,5,6,7,8,9,10)
y <- c(-1,-1,-1,-1,1,-1,1,-1,2,4)
group <- c(1,1,1,2,2,2,3,3,3,3)
DF <- data.frame(x,y,group)

I would like to create a new variable to name the unique individuals, and have a final dataset with a single row per individual (i.e. only have 6 rows instead of 10), that also includes the group information. I.e. if an individual is in all three groups, there could be a value of "111" or if just in the first and last group it would be "101". Any tips?

Thanks for asking about the resulting dataset. I realized my group explanation was bad based on the actual numbers I gave, so I changed the results slightly. Bonus would also be nice to have, but not critical.

name <- c(1,2,3,4,6,8)
group_history <- as.character(c('111','101','100','011','010','001'))
bonus <- as.character(c('1,5,7','2,9','3','4,10','6','8')) 
results_I_want <- data.frame(name,group_history,bonus)

My word, more mistakes fixed above...


回答1:


Using the (updated) example you gave

x <- c(1,2,3,4,5,6,7,8,9,10)
y <- c(-1,-1,-1,-1,1,-1,1,-1,3,4)
group <- c(1,1,1,2,2,2,3,3,3,3)

DF <- data.frame(x,y,group)

Use the x and y to create a mapping from higher numbers to lower numbers that are the same person. Note that names is a string, despite it be a string of digits.

bottom.df <- DF[DF$y==-1,]
mapdown.df <- DF[DF$y!=-1,]
mapdown <- c(mapdown.df$y, bottom.df$x)
names(mapdown) <- c(mapdown.df$x, bottom.df$x)

We don't know how many times it might take to get everything down to the lowest number, so have to use a while loop.

oldx <- DF$x
newx <- mapdown[as.character(oldx)]
while(any(oldx != newx)) {
    oldx = newx
    newx = mapdown[as.character(oldx)]
}

The result is the group it belongs to, names by the lowest number of that set.

DF$id <- unname(newx)

Getting the group membership is harder. Using reshape2 to convert this into wide format (one column per group) where the column is "1" if there was something in that one and "0" if not.

library("reshape2")

wide <- dcast(DF, id~group, value.var="id", 
              fun.aggregate=function(x){if(length(x)>0){"1"}else{"0"}})

Finally, paste these "0"/"1" memberships together to get the grouping variable you described.

wide$grouping = apply(wide[,-1], 1, paste, collapse="")

The result:

> wide
  id 1 2 3 grouping
1  1 1 1 1      111
2  2 1 0 0      100
3  3 1 0 1      101
4  4 0 1 1      011
5  6 0 1 0      010
6  8 0 0 1      001

No "bonus" yet.

EDIT:

To get the bonus information, it helps to redo the mapping to keep everything. If you have a lot of cases, this could be slow.

Replace the oldx/newx part with:

iterx <- matrix(DF$x, ncol=1)
iterx <- cbind(iterx, mapdown[as.character(iterx[,1])])
while(any(iterx[,ncol(iterx)]!=iterx[,ncol(iterx)-1])) {
    iterx <- cbind(iterx, mapdown[as.character(iterx[,ncol(iterx)])])
}

DF$id <- iterx[,ncol(iterx)]

To generate the bonus data, then you can use

bonus <- tapply(iterx[,1], iterx[,ncol(iterx)], paste, collapse=",")
wide$bonus <- bonus[as.character(wide$id)]

Which gives:

> wide
  id 1 2 3 grouping bonus
1  1 1 1 1      111 1,5,7
2  2 1 0 0      100     2
3  3 1 0 1      101   3,9
4  4 0 1 1      011  4,10
5  6 0 1 0      010     6
6  8 0 0 1      001     8

Note this isn't same as your example output, but I don't think your example output is right (how can you have a grouping_history of "000"?)

EDIT:

Now it agrees.




回答2:


Another solution for bonus variable

f_bonus <- function(data=df){
  data_a <- subset(data,y== -1,select=x)
  data_a$pos <- seq(nrow(data_a))
  data_b <- subset(df,y!= -1,select=c(x,y))
  data_b$pos <- match(data_b$y, data_a$x)
  data_t <- rbind(data_a,data_b[-2])
  data_t <- with(data_t,tapply(x,pos,paste,sep="",collapse=","))
  return(data_t)
}


来源:https://stackoverflow.com/questions/8540365/comparing-two-columns-logical-is-value-from-column-1-also-in-column-2

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!