Accessing element of a split string in R - r

If I have a string,
x <- "Hello World"
How can I access the second word, "World", using string split, after
x <- strsplit(x, " ")
x[[2]] does not do anything.

As mentioned in the comments, it's important to realise that strsplit returns a list object. Since your example is only splitting a single item (a vector of length 1) your list is length 1. I'll explain with a slightly different example, inputting a vector of length 3 (3 text items to split):
input <- c( "Hello world", "Hi there", "Back at ya" )
x <- strsplit( input, " " )
> x
[[1]]
[1] "Hello" "world"
[[2]]
[1] "Hi" "there"
[[3]]
[1] "Back" "at" "ya"
Notice that the returned list has 3 elements, one for each element of the input vector. Each of those list elements is split as per the strsplit call. So we can recall any of these list elements using [[ (this is what your x[[2]] call was doing, but you only had one list element, which is why you couldn't get anything in return):
> x[[1]]
[1] "Hello" "world"
> x[[3]]
[1] "Back" "at" "ya"
Now we can get the second part of any of those list elements by appending a [ call:
> x[[1]][2]
[1] "world"
> x[[3]][2]
[1] "at"
This will return the second item from each list element (note that the "Back at ya" input has returned "at" in this case). You can do this for all items at once using something from the apply family. sapply will return a vector, which will probably be good in this case:
> sapply( x, "[", 2 )
[1] "world" "there" "at"
The last value in the input here (2) is passed to the [ operator, meaning the operation x[2] is applied to every list element.
If instead of the second item, you'd like the last item of each list element, we can use tail within the sapply call instead of [:
> sapply( x, tail, 1 )
[1] "world" "there" "ya"
This time, we've applied tail( x, 1 ) to every list element, giving us the last item.
As a preference, my favourite way to apply actions like these is with the magrittr pipe, for the second word like so:
x <- input %>%
strsplit( " " ) %>%
sapply( "[", 2 )
> x
[1] "world" "there" "at"
Or for the last word:
x <- input %>%
strsplit( " " ) %>%
sapply( tail, 1 )
> x
[1] "world" "there" "ya"

Another approach that might be a little easier to read and apply to a data frame within a pipeline (though it takes more lines) would be to wrap it in your own function and apply that.
library(tidyverse)
df <- data.frame(
greetings = c( "Hello world", "Hi there", "Back at ya" )
)
split_params = function (x, sep, n) {
# Splits string into list of substrings separated by 'sep'.
# Returns nth substring.
x = strsplit(x, sep)[[1]][n]
return(x)
}
df = df %>%
mutate(
'greetings' = sapply(
X = greetings,
FUN = split_params,
# Arguments for split_params.
sep = ' ',
n = 2
)
)
df
### (Output in RStudio Notebook)
greetings second_word
<chr> <chr>
Hello world world
Hi there there
Back at ya at
3 rows
###

With stringr 1.5.0, you can use str_split_i to access the ith element of a split string:
library(stringr)
x <- "Hello World"
str_split_i(x, " ", i = 2)
#[1] "World"
It is vectorized:
x <- c("Hello world", "Hi there", "Back at ya")
str_split_i(x, " ", 2)
#[1] "world" "there" "at"

x=strsplit("a;b;c;d",";")
x
[[1]]
[1] "a" "b" "c" "d"
x=as.character(x[[1]])
x
[1] "a" "b" "c" "d"
x=strsplit(x," ")
x
[[1]]
[1] "a"
[[2]]
[1] "b"
[[3]]
[1] "c"
[[4]]
[1] "d"

Related

How to access all sub list elements in R at once? [duplicate]

This question already has answers here:
Select first element of nested list
(5 answers)
R list get first item of each element
(2 answers)
Closed 3 years ago.
I have a splitted string of a vector like
df <- c("Test A:No1", "Test B:No2")
l <- str_split(df, ":")
l
which returns me
[[1]]
[1] "Test A" "No1"
[[2]]
[1] "Test B" "No2"
Now I am interested in accessing all first elements and all last elements independently or create a vector like
[1] "Test A" "Test B"
and
[1] "No1" "No2"
I tried several types of single and double brackets, with and without commas, but l[[x]][1] or l[[x]][2] give me only the list element x.
How can I access all elements at once (e.g. l[[]][1] )?
You may use sapply.
sapply(l, `[`, 1)
# [1] "Test A" "Test B"
sapply(l, `[`, 2)
# [1] "No1" "No2"
Explanation: In R quite everything is a function. Also the parentheses `[` actually are functions. Considering following example makes clear why the sapply above works.
Example
Consider this vector
x <- c("A", "B")
Whey we're doing
x[1]
# [1] "A"
x[2]
# [2] "B"
we're actually applying the special form of the underlying prefix-form of the `[` function:
`[`(x, 1)
# [1] "A"
`[`(x, 2)
# [1] "B"
maybe using unlist and lapply can get the work done.
df <- c("Test A:No1", "Test B:No2")
l <- str_split(df, ":")
> unlist(lapply(l,function(x) x[1]))
[1] "Test A" "Test B"
> unlist(lapply(l,function(x) x[length(x)]))
[1] "No1" "No2"

Split a string on alternating index

I have a string similar to "HLeelmloon" which is two words interweaved together. How can I separate this into two separate words, splitting on alternating letters?
I can use strsplit() and a for loop to allocate alternating letters to two new vectors and then join the list but this seems very long winded:
string <- "HLeelmloon"
split<-el(strsplit(string,''))
> split
[1] "H" "L" "e" "e" "l" "m" "l" "o" "o" "n"
word1<-c()
word2<-c()
for(i in 1:length(split)){
if(i %% 2 == 1){
word1<-append(word1, split[i])
} else {
word2<-append(word2, split[i])
}
}
word1 = paste0(word1, collapse = '')
word2 = paste0(word2, collapse = '')
> word1
[1] "Hello"
> word2
[1] "Lemon"
My issue is it's not very elegant, and it doesn't upscale well if I want to split the string into N different words. Is there a better way to do this?
You could use gsub to capture alternating characters into the same group:
gsub("(.)(.)?", "\\1", string)
#[1] "Hello"
gsub("(.)(.)?", "\\2", string)
#[1] "Lemon"
You can do it by using TRUE and FALSE for indexing, i.e.
v1 = strsplit(string, '')[[1]]
paste(v1[c(TRUE, FALSE)], collapse = '')
#[1] "Hello"
paste(v1[c(FALSE, TRUE)], collapse = '')
#[1] "Lemon"
Considering your question is how to split into more than two words, you should use the split function. Using your example data can be a bit confusing because you chose to name one variable 'split'. In the following block, the first 'split' is the function, the second one your split variable.
number_of_words <- 2
lapply(split(split,1:number_of_words),paste0,collapse='')
$`1`
[1] "Hello"
$`2`
[1] "Lemon"
number_of_words <- 3
lapply(split(split,1:number_of_words),paste0,collapse='')
$`1`
[1] "Heln"
$`2`
[1] "Llo"
$`3`
[1] "emo"
To avoid confusion, here's the same code without the variable named split:
number_of_words <- 2
lapply(split(el(strsplit(string,'')),1:number_of_words),paste0,collapse='')
$`1`
[1] "Hello"
$`2`
[1] "Lemon"
Try this code:
paste0(split[seq(1,nchar(string),by = 2)],collapse="")
[1] "Hello"
> paste0(split[seq(2,nchar(string),by = 2)],collapse="")
[1] "Lemon"
It appends even and odd positions in the string string
Another way using your split variable, will work with any number of words:
N <- 2
apply(matrix(split,N),1,paste,collapse="")
# [1] "Hello" "Lemon"

Splitting a string in R

Consider:
x<-strsplit("This is an example",split="\\s",fixed=FALSE)
I am surprised to see that x has length 1 rather than length 4:
> length(x)
[1] 1
like this, x[3] is null. But If I unlist, then:
> x<-unlist(x)
> x
[1] "This" "is" "an" "example"
> length(x)
[1] 4
only now x[3] is "an".
Why wasn't that list originally by length 4 so that elements can be accessed by indexing? This gives troubles to access the splitted elements, since I have to unlist first.
This allows strsplit to be vectorized for its input argument. For instance, it will allow you to split a vector such as:
x <- c("string one", "string two", "and string three")
into a list of split results.
You do not need to unlist, but rather, you can refer to the element by a combination of its list index and the vector index. For instance, if you wanted to get the second word in the second item, you can do:
> x <- c("string one", "string two", "and string three")
> y <- strsplit(x, "\\s")
> y[[2]][2]
[1] "two"
That's because strsplit generates a list containing each element (word).
Try
> x[[1]]
#[1] "This" "is" "an" "example"
and
> length(x[[1]])
#[1] 4

using paste with a list

I'm trying to understand the behavior of strsplit and paste, which are inverse functions. However, when I strsplit a vector, a list is returned, like so:
> strsplit(c("on,e","tw,o","thre,e","fou,r"),",")
[[1]]
[1] "on" "e"
[[2]]
[1] "tw" "o"
[[3]]
[1] "thre" "e"
[[4]]
[1] "fou" "r"
I tried using lapply to cat the elements of the list back together, but it doesn't work:
> lapply(strsplit(c("on,e","tw,o","thre,e","fou,r"),","),cat)
on etw othre efou r[[1]]
NULL
[[2]]
NULL
[[3]]
NULL
[[4]]
NULL
The same formula with paste instead of cat actually does nothing at all! Why am I getting these results? and how can I get the result I want, which is the original vector back again?
(Obviously, in my actual code I'm trying to do more with the strsplit and cat than just return the original vector, but I think a solution to this problem will work for mine. Thanks!)
While yes, cat will concatenate and print to the console, it does not actually function in the same way paste does. It's result best explained in help("cat")
The collapse argument in paste is effectively the opposite of the split argument in strsplit. And you can use sapply to return the simplified pasted vector.
x <- c("on,e","tw,o","thre,e","fou,r")
( y <- sapply(strsplit(x, ","), paste, collapse = ",") )
# [1] "on,e" "tw,o" "thre,e" "fou,r"
( z <- vapply(strsplit(x, ","), paste, character(1L), collapse = ",") )
# [1] "on,e" "tw,o" "thre,e" "fou,r"
identical(x, y)
# [1] TRUE
identical(x, z)
# [1] TRUE
Note that for cases like this, vapply will be more efficient than sapply. And adding fixed = TRUE in strsplit should increase efficiency as well.

R: cut string of characters in vectors

let x be the vector
[1] "hi" "hello" "Nyarlathotep"
Is it possible to produce a vector, let us say y, from x s.t. its components are
[1] "hi" "hello" "Nyarl"
?
In other words, I would need a command in R which cuts strings of text to a given length (in the above, length=5).
Thanks a lot!
More obvious than substring to me would be strtrim:
> x <- c("hi", "hello", "Nyarlathotep")
> x
[1] "hi" "hello" "Nyarlathotep"
> strtrim(x, 5)
[1] "hi" "hello" "Nyarl"
substring is great for extracting data from within a string at a given position, but strtrim does exactly what you're looking for.
The second argument is widths and that can be a vector of widths the same length as the input vector, in which case, each element can be trimmed by a specified amount.
> strtrim(x, c(1, 2, 3))
[1] "h" "he" "Nya"
Use substring see details in ?substring
> x <- c("hi", "hello", "Nyarlathotep")
> substring(x, first=1, last=5)
[1] "hi" "hello" "Nyarl"
Last update
You can also use sub with regex
> sub("(.{5}).*", "\\1", x)
[1] "hi" "hello" "Nyarl"
A (probably) faster alternative is sprintf():
sprintf("%.*s", 5, x)
[1] "hi" "hello" "Nyarl"

Resources