I have the following data frame and I want to populate the column "TargetPos" with the name of the column whose value matches that of the column "Item" (so "Left" in this case) (in my actual data, I actually have four positions: TopRight, TopLeft, BottomRight, BottomLeft). What approach can one use?
| Item | Right | Left | TargetPos |
|---|---|---|---|
| bed.png | flag.png | bed.png |
I've been trying something along these lines:
df$TargetPos <- lapply(df$Item, function(x)
if (x == df$Right) {
names(df$Right)
} else {
Left
})
CodePudding user response:
Since you want to have vector as output, you should use sapply instead of lapply.
The core of the solution is to use which to output the column index which has value(s) matching the Item column (remember the 1, since the Item column itself is not included when indexing), and output the colnames.
I have created some extra rows for demonstration purpose.
Input
df <- data.frame(Item = c("bed.png", "a", "b", "e", "532523"),
Right = c("flag.png", "a", "c", "e", "aaa"),
Left = c("bed.png", "b", "b", "e", "bbb"))
Item Right Left
1 bed.png flag.png bed.png
2 a a b
3 b c b
4 e e e
5 532523 aaa bbb
Code
df$TargetPos <-
sapply(1:nrow(df),
function(x) colnames(df)[which(df[x, -1] == df$Item[x]) 1])
df
Item Right Left TargetPos
1 bed.png flag.png bed.png Left
2 a a b Right
3 b c b Left
4 e e e Right, Left
5 532523 aaa bbb
CodePudding user response:
Data from @benson23 (many thanks).
Here is a tidyverse approach:
library(dplyr)
library(tidyr)
df %>%
mutate(across(c(Right, Left), ~case_when(. == Item ~ cur_column(),
TRUE ~ NA_character_), .names = 'new_{col}')) %>%
unite(TargetPos, starts_with('new'), na.rm = TRUE, sep = ', ')
Item Right Left TargetPos
1 bed.png flag.png bed.png Left
2 a a b Right
3 b c b Left
4 e e e Right, Left
5 532523 aaa bbb
CodePudding user response:
In base R, we may also use apply
df$TargetPos <- apply(df$Item == df[-1], 1, \(x) toString(names(x)[x]))
-output
> df
Item Right Left TargetPos
1 bed.png flag.png bed.png Left
2 a a b Right
3 b c b Left
4 e e e Right, Left
5 532523 aaa bbb
data
df <- structure(list(Item = c("bed.png", "a", "b", "e", "532523"),
Right = c("flag.png", "a", "c", "e", "aaa"), Left = c("bed.png",
"b", "b", "e", "bbb")), class = "data.frame", row.names = c(NA,
-5L))
CodePudding user response:
You could exploit the vectorization of == more. Basically it's df1$Item == df1[2:5] gives a boolean matrix then apply which over MARGIN=1 i.e. over the rows.
So if your data looks like this
df1
# Item TopRight TopLeft BottomRight BottomLeft
# 1 bed.png flag.png bed.png stars.png stripes.png
# 2 stripes.png flag.png bed.png stars.png stripes.png
# 3 foo.png foo.png bar.png baz.png bam.png
# 4 baz.png foo.png bar.png baz.png bam.png
you may do:
transform(df1, target=names(df1)[2:5][apply(Item == df1[2:5], 1, which)])
# Item TopRight TopLeft BottomRight BottomLeft target
# 1 bed.png flag.png bed.png stars.png stripes.png TopLeft
# 2 stripes.png flag.png bed.png stars.png stripes.png BottomLeft
# 3 foo.png foo.png bar.png baz.png bam.png TopRight
# 4 baz.png foo.png bar.png baz.png bam.png BottomRight
If it rather looks like that
df2
# Item TopRight TopLeft BottomRight BottomLeft
# 1 bed.png flag.png bed.png stars.png stripes.png
# 2 stripes.png flag.png bed.png stars.png stripes.png
# 3 foo.png foo.png bar.png baz.png bam.png
# 4 baz.png foo.png bar.png baz.png bam.png
# 5 -1 foo.png bar.png baz.png bam.png
# 6 -1 -1 -1 -1 -1
you could do:
within(df2, {
target <- Vectorize(toString)(apply(Item == df2[2:5], 1, \(x)
names(which(x))))
target <- replace(target, target == "", NA)
})
df2
# Item TopRight TopLeft BottomRight BottomLeft target
# 1 bed.png flag.png bed.png stars.png stripes.png TopLeft
# 2 stripes.png flag.png bed.png stars.png stripes.png BottomLeft
# 3 foo.png foo.png bar.png baz.png bam.png TopRight
# 4 baz.png foo.png bar.png baz.png bam.png BottomRight
# 5 -1 foo.png bar.png baz.png bam.png <NA>
# 6 -1 -1 -1 -1 -1 TopRight, TopLeft, BottomRight, BottomLeft
Data:
df1 <- structure(list(Item = c("bed.png", "stripes.png", "foo.png",
"baz.png"), TopRight = c("flag.png", "flag.png", "foo.png", "foo.png"
), TopLeft = c("bed.png", "bed.png", "bar.png", "bar.png"), BottomRight = c("stars.png",
"stars.png", "baz.png", "baz.png"), BottomLeft = c("stripes.png",
"stripes.png", "bam.png", "bam.png")), class = "data.frame", row.names = c(NA,
-4L))
df2 <- structure(list(Item = c("bed.png", "stripes.png", "foo.png",
"baz.png", "-1", "-1"), TopRight = c("flag.png", "flag.png",
"foo.png", "foo.png", "foo.png", "-1"), TopLeft = c("bed.png",
"bed.png", "bar.png", "bar.png", "bar.png", "-1"), BottomRight = c("stars.png",
"stars.png", "baz.png", "baz.png", "baz.png", "-1"), BottomLeft = c("stripes.png",
"stripes.png", "bam.png", "bam.png", "bam.png", "-1")), class = "data.frame", row.names = c(NA,
-6L))
