This question already has answers here:
Create grouping variable for consecutive sequences and split vector
(5 answers)
Closed 3 years ago.
If I have a vector as such:
dat <- c(1,2,3,4,5,19,20,21,56,80,81,92)
How can I break it up into a list as:
[[1]]
1 2 3 4 5
[[2]]
19 20 21
[[3]]
56
[[4]]
80 81
[[5]]
92
Just use split in conjunction with diff:
> split(dat, cumsum(c(1, diff(dat) != 1)))
$`1`
[1] 1 2 3 4 5
$`2`
[1] 19 20 21
$`3`
[1] 56
$`4`
[1] 80 81
$`5`
[1] 92
Not exactly what you asked for, but the "R.utils" package has a couple of related fun functions:
library(R.utils)
seqToIntervals(dat)
# from to
# [1,] 1 5
# [2,] 19 21
# [3,] 56 56
# [4,] 80 81
# [5,] 92 92
seqToHumanReadable(dat)
# [1] "1-5, 19-21, 56, 80-81, 92"
I think Robert Krzyzanowski is correct. So here is a tidyverse that involves placing the vector into a tibble (data frame).
library(tidyverse)
# library(dplyr)
# library(tidyr)
df <- c(1,2,3,4,5,19,20,21,56,80,81,92) %>%
tibble(dat = .)
# using lag()
df %>%
group_by(seq_id = cumsum(dat != lag(dat) + 1 | is.na(dat != lag(dat) + 1)) %>%
nest()
# using diff()
df %>%
group_by(seq_id = cumsum(c(1, diff(dat)) != 1)) %>%
nest()
Of course, you need not nest the resulting groups into list-columns, and can instead perform some kind of summary operation.
Related
Problem:
I have a list of two lists of three vectors. I would like to remove the zero vector from each sublist.
Example:
x <- list(x1=c(0,0,0), x2=c(3,4,5), x3=c(45,34,23))
y <- list(y1=c(2,33,4), y2=c(0,0,0), y3=c(4,5,44))
z <- list(x, y)
Try:
I tried this:
res <- lapply(1:2, function(i) {lapply(1:3, function(j) z[[i]][[j]][z[[i]][[j]] != 0])})
Which gave me this:
> res
[[1]]
[[1]][[1]]
numeric(0)
[[1]][[2]]
[1] 3 4 5
[[1]][[3]]
[1] 45 34 23
[[2]]
[[2]][[1]]
[1] 2 33 4
[[2]][[2]]
numeric(0)
[[2]][[3]]
[1] 4 5 44
Problem with the output:
I do not want numeric(0).
Expected output:
x= list(x2, x3)
y=list(y1, y3)
Any idea, please?
You can try a tidyverse if the nested list structure is not important
library(tidyverse)
z %>%
flatten() %>%
keep(~all(. != 0))
$x2
[1] 3 4 5
$x3
[1] 45 34 23
$y1
[1] 2 33 4
$y3
[1] 4 5 44
Given your structure of list of lists I would go with the following:
filteredList <- lapply(z, function(i) Filter(function(x) any(x != 0), i))
x <- filteredList[[1]]
y <- filteredList[[2]]
x
##$`x2`
##[1] 3 4 5
##$x3
##[1] 45 34 23
y
##$`y1`
##[1] 2 33 4
##$y3
##[1] 4 5 44
define z as
z <- c(x, y)
# z <- unlist(z, recursive = F) if you cannot define z by yourself.
then use:
z[sapply(z, any)]
#$`x2`
#[1] 3 4 5
#$x3
#[1] 45 34 23
#$y1
#[1] 2 33 4
#$y3
#[1] 4 5 44
Please note:
As in the tradition of lang C. Every integer/ numeric != 0 will be casted to TRUE. So in this task we can use this logic. ?any will eval FALSE if all values are 0.
Or:
x <- list(x1=c(0,0,0), x2=c(3,4,5), x3=c(45,34,23))
y <- list(y1=c(2,33,4), y2=c(0,0,0), y3=c(4,5,44))
z <- list(x, y)
lapply(z, function(a) a[unlist(lapply(a, function(b) !identical(b, rep(0,3))))])
#[[1]]
#[[1]]$`x2`
#[1] 3 4 5
#
#[[1]]$x3
#[1] 45 34 23
#
#
#[[2]]
#[[2]]$`y1`
#[1] 2 33 4
#
#[[2]]$y3
#[1] 4 5 44
with purrr it can be really compact
library(purrr)
map(z, keep ,~all(.!=0))
# [[1]]
# [[1]]$x2
# [1] 3 4 5
#
# [[1]]$x3
# [1] 45 34 23
#
#
# [[2]]
# [[2]]$y1
# [1] 2 33 4
#
# [[2]]$y3
# [1] 4 5 44
If it wasn't for the annoying warnings we could do just map(z, keep , all)
I have the following list of numbers (1,3,4,5,7,9,10,12,15) and I want to find out all the possible combinations of 3 numbers from this list that would sum to 20.
My research on stackoverflow has led me to this post:
Finding all possible combinations of numbers to reach a given sum
There is a solution provided by Mark which stand as follows:
subset_sum = function(numbers,target,partial=0){
if(any(is.na(partial))) return()
s = sum(partial)
if(s == target) print(sprintf("sum(%s)=%s",paste(partial[-1],collapse="+"),target))
if(s > target) return()
for( i in seq_along(numbers)){
n = numbers[i]
remaining = numbers[(i+1):length(numbers)]
subset_sum(remaining,target,c(partial,n))
}
}
However I am having a hard time trying to tweak this set of codes to match my problem. Or may be there is a simpler solution?
I want the output in R to show me the list of numbers.
Any help would be appreciated.
You can use combn function and filter to meet your criteria. I have performed below calculation in 2 steps but one can perform it in single step too.
v <- c(1,3,4,5,7,9,10,12,15)
AllComb <- combn(v, 3) #generates all combination taking 3 at a time.
PossibleComb <- AllComb[,colSums(AllComb) == 20] #filter those with sum == 20
#Result: 6 sets of 3 numbers (column-wise)
PossibleComb
# [,1] [,2] [,3] [,4] [,5] [,6]
# [1,] 1 1 1 3 3 4
# [2,] 4 7 9 5 7 7
# [3,] 15 12 10 12 10 9
#
# Result in list
split(PossibleComb, col(PossibleComb))
# $`1`
# [1] 1 4 15
#
# $`2`
# [1] 1 7 12
#
# $`3`
# [1] 1 9 10
#
# $`4`
# [1] 3 5 12
#
# $`5`
# [1] 3 7 10
#
# $`6`
# [1] 4 7 9
The combn also have a FUN parameter which we can describe to output as list and then Filter the list elements based on the condition
Filter(function(x) sum(x) == 20, combn(v, 3, FUN = list))
#[[1]]
#[1] 1 4 15
#[[2]]
#[1] 1 7 12
#[[3]]
#[1] 1 9 10
#[[4]]
#[1] 3 5 12
#[[5]]
#[1] 3 7 10
#[[6]]
#[1] 4 7 9
data
v <- c(1,3,4,5,7,9,10,12,15)
This question already has an answer here:
mapply over two lists [closed]
(1 answer)
Closed 6 years ago.
I have a dataframe df looking like this
A B C D
1 78 12 43 12
2 23 12 42 13
3 14 42 11 99
4 49 94 27 72
I need the first two columns converted into a list which looks exactly like this:
[[1]]
[1] 78 12
[[2]]
[1] 23 12
[[3]]
[1] 14 42
[[4]]
[1] 49 94
Basically what
list(c(78, 12), c(23, 12), c(14, 42), c(49, 94)
would do. I tried this
lapply(as.list(1:dim(df)[1]), function(x) df[x[1],])
as well as
lapply(as.list(1:nrow(df)), function(x) df)
But thats slightly different.
Any suggestions?
You can try the Map:
Map(c, df$A, df$B)
[[1]]
[1] 78 12
[[2]]
[1] 23 12
[[3]]
[1] 14 42
[[4]]
[1] 49 94
In case this is of interest, it is possible to accomplish this with the foreach package:
library(foreach)
foreach(i=seq.int(nrow(df))) %do% (c(df[[i]][1], df[[i]][2]))
foreach returns a list by default. The code runs down the rows and pulls elements from the first and second columns.
An even cleaner to read version:
foreach(i=seq.int(nrow(df))) %do% (df[[i]][1:2])
Another option is with lapply
lapply(seq_len(nrow(df1)), function(i) unlist(df1[i, 1:2], use.names=FALSE))
#[[1]]
#[1] 78 12
#[[2]]
#[1] 23 12
#[[3]]
#[1] 14 42
#[[4]]
#[1] 49 94
This question already has answers here:
Create grouping variable for consecutive sequences and split vector
(5 answers)
Closed 5 years ago.
The following vector x contains the two sequences 1:4 and 6:7, among other non-sequential digits.
x <- c(7, 1:4, 6:7, 9)
I'd like to split x by its sequences, so that the result is a list like the following.
# [[1]]
# [1] 7
#
# [[2]]
# [1] 1 2 3 4
#
# [[3]]
# [1] 6 7
#
# [[4]]
# [1] 9
Is there a quick and simple way to do this?
I've tried
split(x, c(0, diff(x)))
which gets close, but I don't feel like appending 0 to the differenced vector is the right way to go. Using findInterval didn't work either.
split(x, cumsum(c(TRUE, diff(x)!=1)))
#$`1`
#[1] 7
#
#$`2`
#[1] 1 2 3 4
#
#$`3`
#[1] 6 7
#
#$`4`
#[1] 9
Just for fun, you can make use of Carl Witthoft's seqle function from his "cgwtools" package. (It's not going to be anywhere near as efficient as Roland's answer.)
library(cgwtools)
## Here's what seqle does...
## It's like rle, but for sequences
seqle(x)
# Run Length Encoding
# lengths: int [1:4] 1 4 2 1
# values : num [1:4] 7 1 6 9
y <- seqle(x)
split(x, rep(seq_along(y$lengths), y$lengths))
# $`1`
# [1] 7
#
# $`2`
# [1] 1 2 3 4
#
# $`3`
# [1] 6 7
#
# $`4`
# [1] 9
This question already has answers here:
Create grouping variable for consecutive sequences and split vector
(5 answers)
Closed 4 years ago.
If I have a vector as such:
dat <- c(1,2,3,4,5,19,20,21,56,80,81,92)
How can I break it up into a list as:
[[1]]
1 2 3 4 5
[[2]]
19 20 21
[[3]]
56
[[4]]
80 81
[[5]]
92
Just use split in conjunction with diff:
> split(dat, cumsum(c(1, diff(dat) != 1)))
$`1`
[1] 1 2 3 4 5
$`2`
[1] 19 20 21
$`3`
[1] 56
$`4`
[1] 80 81
$`5`
[1] 92
Not exactly what you asked for, but the "R.utils" package has a couple of related fun functions:
library(R.utils)
seqToIntervals(dat)
# from to
# [1,] 1 5
# [2,] 19 21
# [3,] 56 56
# [4,] 80 81
# [5,] 92 92
seqToHumanReadable(dat)
# [1] "1-5, 19-21, 56, 80-81, 92"
I think Robert Krzyzanowski is correct. So here is a tidyverse that involves placing the vector into a tibble (data frame).
library(tidyverse)
# library(dplyr)
# library(tidyr)
df <- c(1,2,3,4,5,19,20,21,56,80,81,92) %>%
tibble(dat = .)
# using lag()
df %>%
group_by(seq_id = cumsum(dat != lag(dat) + 1 | is.na(dat != lag(dat) + 1)) %>%
nest()
# using diff()
df %>%
group_by(seq_id = cumsum(c(1, diff(dat)) != 1)) %>%
nest()
Of course, you need not nest the resulting groups into list-columns, and can instead perform some kind of summary operation.