JCB
JCB

Reputation: 35

R: How to paste a full vector within another vector

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

Answers (3)

Rodrigo de Alexandre
Rodrigo de Alexandre

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

akrun
akrun

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))

Update

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

data

df <- mtcars
df$Cars <- rownames(df)

SearchA = c("mazda","honda")
Search1 = c("merc","toyota")

allSearches = c(SearchA,Search1)

Upvotes: 0

Cath
Cath

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

Related Questions