I have a vector a with values (1,2,3,4) and another vector b with values (1,1,0,1). Using the elements in b as a flag, I want to remove the vector elements from A at the same positions where 0 is found in element b.
a <- c(1,2,3,4)
b <- c(1,1,0,1)
for(i in 1:length(b))
{
if(b[i] == 0)
{
a <- a[-i]
}
}
I get the desired output
a
[1] 1 2 4
But using ifelse, I do not get the output as required.
a <- c(1,2,3,4)
b <- c(1,1,0,1)
for(i in 1:length(b))
{
a <- ifelse(b[i] == 0,a[-i],a)
}
Output:
a
[1] 1
How to use ifelse in such situations?
I think ifelse isn't the correct function here since ifelse gives output of same length as input and we want to subset values here. You don't need a loop as well. You can directly do
a[b != 0]
#[1] 1 2 4
data
a <- 1:4
b <- c(1, 1, 0, 1)
Another option could be:
a[as.logical(b)]
[1] 1 2 4
If you want to use ifelse, you can use the following code
na.omit(ifelse(b==0,NA,a))
such that
> na.omit(ifelse(b==0,NA,a))
[1] 1 2 4
attr(,"na.action")
[1] 3
attr(,"class")
[1] "omit"
We can also use double negation
a[!!b]
#[1] 1 2 4
data
a <- 1:4
b <- c(1, 1, 0, 1)
Related
I have a vector like below
tmp <- c(a=1, b=2, c=3)
a b c
1 2 3
I want to flatten this vector to get only 1, 2, 3.
I tried unlist(tmp) but it still gives me the same result.
How to achieve that efficiently?
You just want to remove the names attribute from tmp. There are a number of ways to do that.
You can unname it.
unname(tmp)
# [1] 1 2 3
Or use a very common method for removing names, by setting them to NULL.
names(tmp) <- NULL
Or strip the attributes with as.vector.
as.vector(tmp)
# [1] 1 2 3
Or re-concatenate it without the names.
c(tmp, use.names=FALSE)
# [1] 1 2 3
Or use setNames.
setNames(tmp, NULL)
# [1] 1 2 3
There is a use case that the above does not cover:
tmp <- c(1,2,3)
names(tmp) <- c("a","b","c")
In this case you need to use both:
unlist(unname(tmp))
I've like to remove elements in a list, if the number of elements are smaller than 3.
For this I try:
#Create a list
my_list <- list(a = c(3,5,6), b = c(3,1,0), c = 4, d = NA)
my_list
$a
[1] 3 5 6
$b
[1] 3 1 0
$c
[1] 4
$d
[1] NA
# Thant I create a function for remove the elements by my condition:
delete.F <- function(x.list){
x.list[unlist(lapply(x.list, function(x) ncol(x)) < 3)]}
delete.F(my_list)
And I have as output:
Error in unlist(lapply(x.list, function(x) ncol(x)) < 3) :
(list) object cannot be coerced to type 'double'
Any ideas, please?
An option is to create a logical expression with lengths and use that for subsetting the list
my_list[lengths(my_list) >=3]
#$a
#[1] 3 5 6
#$b
#[1] 3 1 0
Note that in the example, it is a list of vectors and not a list of data.frame. the ncol/nrow is when there is a dim attribute - matrix checks TRUE for that, as do data.frame
If we want to somehow use lapply (based on some constraints), create the logic with length
unlist(lapply(my_list, function(x) if(length(x) >=3 ) x))
If we need to create the index with lapply, use length (but it would be slower than lengths)
my_list[unlist(lapply(my_list, length)) >= 3]
Here are few more options. Using Filter in base R
Filter(function(x) length(x) >=3, my_list)
#$a
#[1] 3 5 6
#$b
#[1] 3 1 0
Or using purrr's keep and discard
purrr::keep(my_list, ~length(.) >= 3)
purrr::discard(my_list, ~length(.) < 3)
I'm starting with a data.frame similar to this in R:
data <- data.frame(Names=c("A", "B", "C", "D"), E1=c(NA, 1, 0, 4), E2=c(3, 0, 0, NA))
Names E1 E2
1 A NA 3
2 B 1 0
3 C 0 0
4 D 4 NA
My goal is to create a list that shows the Names where the value of each column is nonzero, zero, or NA. In other words:
[[1]]
$Nonzero
"B", "D"
$Zero
"C"
$N/A
"A"
[[2]]]
$Nonzero
"A"
$Zero
"B", "C"
$N/A
"D"
So far I've written the following function:
my.function <- function(x) {
nonzero <- which(x!=0 & !is.na(x))
zero <- which(x==0 & !is.na(x))
na <- which(is.na(x))
rows <- list("Nonzero"=nonzero, "Zero"=zero, "N/A"=na)
return(rows)
}
Then I used lapply:
lapply(data[,-1], my.function)
The result is this:
[[1]]
$Nonzero
2, 4
$Zero
3
$N/A
1
[[2]]]
$Nonzero
1
$Zero
2, 3
$N/A
4
So I've got the row numbers, but now I can't figure out how to get to the Names from here. My real data set has ~50 columns so I definitely need some thing I can use with lapply, rather than doing it separately for each column. Advice is greatly appreciated!
Edit: I should add that I would like this function to be transferable for use on other datasets. Thus inserting the name of this individual dataset into the function will not work.
A very quick fix is:
library(magrittr)
my.function <- function(x) {
nonzero <- which(x!=0 & !is.na(x)) %>% data$Names[.]
zero <- which(x==0 & !is.na(x)) %>% data$Names[.]
na <- which(is.na(x)) %>% data$Names[.]
rows <- list("Nonzero"=nonzero, "Zero"=zero, "N/A"=na)
return(rows)
}
Then call
lapply(data, my.function)[-1]
Because you don't want the list results for column "Names".
If I have the following list:
a <- list(1:3, 4:5, 6:9)
a
[[1]]
[1] 1 2 3
[[2]]
[1] 4 5
[[3]]
[1] 6 7 8 9
I want to determine which element of the list a specific value is in. For example, I might want to find which element the number 5 falls under. In this case it would be [[2]].
My goal is to have something like
match(5,a)
return the value 2.
However, this code only checks whether the selected number exists as a complete element of a given element
match(5,a)
[1] NA
Further, unlist only tells me where in the entire length of all values my number of interest falls:
match(5,unlist(a))
[1] 5
Thoughts?
You can use grep function
grep(5, a)
# [1] 2
grep(9, a)
# [1] 3
Updated Answer
After reading #nicola 's comment came to know that grep command works only for the numbers that belong to start and end of the list and not for the numbers that are in between.
You can try the below mentioned code for the complete solution,
a <- list(1:3, 4:5, 6:9)
df <- data.frame(unlist(a))
df$group <- 0
k <- 1
i<-0
for(i in 1:length(a))
{
x[i] <- length(unlist(a[i]))
for(j in 1:x[i])
{
df$group[k] <- i
k <- k+1
}
}
colnames(df)[1] <- "num"
df[df$num == 5, ]$group
# [1] 2
> df[df$num == 9, ]$group
#[1] 3
df[df$num == 8, ]$group
# [1] 3
I have a dataset which contains positive, negative as well as NA values. How could I select positive-only values using a script? I would also like to replace negatives numbers with NA and leave NA values as they are.
You could use the which function:
sample <- c(1, 2, -7, NA, NaN)
sample[which(sample > 0)]
[1] 1 2
For negative values assign NA.
Using which:
sample[which(sample < 0)] <- NA
You could try the following command:
> x<-c(1,2,3,-5)
> x[x>0]
[1] 1 2 3
would return all the positive values.
To replace negative numbers with NA use
> x <- ifelse(x<0, NA,x)
> x
[1] 1 2 3 NA
Another way to select positive values would be to use sign
x[sign(x) == 1]
and we can combine both these in Filter
Filter(function(i) i > 0, x)
Filter(function(i) sign(i) == 1, x)