Reputation: 35
I'm trying to use strings in a vector to call out another vector so that everything will output in the paste command. I'm then using these character vectors to filter out a dataframe.
Using the mtcars data frame as an example (converting the row names to a column so that I have some characters to work with)...
df <- mtcars
df$Cars <- rownames(df)
I have all my searches of interest in the following vector
allSearches = c("SearchA","Search1")
and "SearchA" and "Search1" are defined such that...
SearchA = c("mazda","honda")
Search1 = c("merc","toyota")
I want to be able to filter data frame df by variables in SearchA, then by variables in Search1.
For individual lines of code, I can get the following to work...
sub = df[grepl(paste(Search1,collapse="|"), df$Cars, ignore.case=T),]
sub$SearchA <- "Yes"
df = merge(df, sub, all.x = T)
The goal of this is to have it in a "for" loop so that I can just add additional searches when needed.
I've attempted the following...
for (i in 1:length(allSearches)){
sub = df[grepl(paste(allSearches[i],collapse="|"), df$Cars, ignore.case=T),]
sub[,allSearches[i]] <- "Yes"
df = merge(df, sub, all.x = T)
}
but I get the following error...
Error in
[<-.data.frame
(*tmp*
, , allSearches[i], value = "Yes") :replacement has 1 rows, data has 0
While trying to dissect the issue, I found that the issue for this particular method lies within the "paste" function where...
paste(allSearches[1],collapse="|")
Outputs the following...
"SearchA"
Instead of the full vector
[1] "mazda" "honda"
Any help would be greatly appreciated. I'm dealing with quite a few "Searches" that fit inside of "allSearches" so it would be nice to get everything within a loop so that I do not have to do 3 lines for each filter. Also, the vector names of the different "Searches" do not follow any particular pattern.
Thanks! JCB
Upvotes: 1
Views: 245
Reputation: 662
Ok this is what you need to change.
df <- mtcars
df$Cars <- rownames(df)
SearchA = c("mazda","honda")
Search1 = c("merc","toyota")
allSearches = c("SearchA","Search1")
for (i in 1:length(allSearches)){
sub = df[grepl(paste(get(allSearches[i]), collapse="|"), df$Cars, ignore.case=T),]
sub[,allSearches[i]] <- "Yes"
df = merge(df, sub, all.x = T)
}
the only thing I've changed was paste(get(allSearches[i]), collapse="|")
. When you use get
it tries to fetch vectors that contain those names
also at the end you could put:
df[is.na(df)] <- "No"
Upvotes: 0
Reputation: 887118
You could also use Map
df[allSearches] <-Map(function(x,y)
c('No', 'Yes')[grepl(x,y, ignore.case=TRUE)+1] , allSearches, list(df$Cars))
If you need to merge
across groups
of Seach
patterns
allSearches <- mget(ls(pattern='^Search'))
res <- merge(df, Reduce(function(...) merge(..., all=TRUE),
Map(function(x,y,z) {indx <-grepl(paste(x, collapse="|"), y,
ignore.case=TRUE)
sub<- df[indx,]
sub[z] <- 'Yes'
sub } ,
allSearches, list(df$Cars), names(allSearches))),
all.x=TRUE)
res[c(6:13,18:20,29),]
# mpg cyl disp hp drat wt qsec vs am gear carb Cars Search1
#6 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8 Maserati Bora <NA>
#7 15.2 8 275.8 180 3.07 3.780 18.00 0 0 3 3 Merc 450SLC Yes
#8 15.2 8 304.0 150 3.15 3.435 17.30 0 0 3 2 AMC Javelin <NA>
#9 15.5 8 318.0 150 2.76 3.520 16.87 0 0 3 2 Dodge Challenger <NA>
#10 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4 Ford Pantera L <NA>
#11 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3 Merc 450SE Yes
#12 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3 3 Merc 450SL Yes
#13 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4 Merc 280C Yes
#18 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6 Ferrari Dino <NA>
#19 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4 Mazda RX4 <NA>
#20 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4 Mazda RX4 Wag <NA>
#29 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2 Honda Civic <NA>
# SearchA
#6 <NA>
#7 <NA>
#8 <NA>
#9 <NA>
#10 <NA>
#11 <NA>
#12 <NA>
#13 <NA>
#18 <NA>
#19 Yes
#20 Yes
#29 Yes
df <- mtcars
df$Cars <- rownames(df)
SearchA = c("mazda","honda")
Search1 = c("merc","toyota")
allSearches = c(SearchA,Search1)
Upvotes: 0
Reputation: 24074
You can also keep your former code and just use get
:
In your loop, if you replace
sub = df[grepl(paste(allSearches[i],collapse="|"), df$Cars, ignore.case=T),]
by
sub = df[grepl(paste(get(allSearches[i]),collapse="|"), df$Cars, ignore.case=T),]
It should work.
for (i in 1:length(allSearches)){
sub = df[grepl(paste(get(allSearches[i]),collapse="|"), df$Cars, ignore.case=T),]
sub[,allSearches[i]] <- "Yes"
df = merge(df, sub, all.x = T)
}
> df[c(6:13,18:20,29),]
mpg cyl disp hp drat wt qsec vs am gear carb Cars SearchA Search1
6 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8 Maserati Bora <NA> <NA>
7 15.2 8 275.8 180 3.07 3.780 18.00 0 0 3 3 Merc 450SLC <NA> Yes
8 15.2 8 304.0 150 3.15 3.435 17.30 0 0 3 2 AMC Javelin <NA> <NA>
9 15.5 8 318.0 150 2.76 3.520 16.87 0 0 3 2 Dodge Challenger <NA> <NA>
10 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4 Ford Pantera L <NA> <NA>
11 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3 Merc 450SE <NA> Yes
12 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3 3 Merc 450SL <NA> Yes
13 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4 Merc 280C <NA> Yes
18 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6 Ferrari Dino <NA> <NA>
19 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4 Mazda RX4 Yes <NA>
20 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4 Mazda RX4 Wag Yes <NA>
29 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2 Honda Civic Yes <NA>
Upvotes: 0