Group_by - retain rows based on value in below row of dataset - r

Hi I am looking to retain rows in a dataset similar to the below:
ID
Value1
Value2
A
1
0
A
0
1
A
1
1
A
0
1
A
0
0
A
0
0
A
1
0
A
1
1
A
0
1
Where 'Value1' = 1 and 'Value2' in the immediate below row = 1. Under these conditions both rows should be retained; any other rows corresponding to ID 'A' should not be retained. Can anyone help with this please? In this example the below output should be returned:
ID
Value1
Value2
A
1
0
A
0
1
A
1
1
A
0
1
A
1
0
A
1
1
A
0
1

The logic is keep all the rows where row before has Value1=1 and row immediately after has Value2=1. I've added a few rows to your data to check different scenarios.
df=structure(list(ID = c("A", "A", "A", "A", "A", "A", "A", "A",
"A"), Value1 = c(1L, 0L, 0L, 1L, 0L, 0L, 1L, 0L, 0L), Value2 = c(0L,
1L, 0L, 0L, 0L, 1L, 0L, 1L, 1L)), class = "data.frame", row.names = c(NA,
-9L))
ID Value1 Value2
1 A 1 0
2 A 0 1
3 A 0 0
4 A 1 0
5 A 0 0
6 A 0 1
7 A 1 0
8 A 0 1
9 A 0 1
edit: your edit requires you to distinguish between 1's in Value1 and Value2 columns, there are probably a number of options available here, one option is to say that if Value=1 then this starts a new sequence, so the next row needs to have Value2=1 and Value1!=1.
tmp=which((df$Value1==1)+c(tail(df$Value1!=1 & df$Value2==1,-1),NA)==2)
df[sort(c(tmp,tmp+1)),]
ID Value1 Value2
1 A 1 0
2 A 0 1
7 A 1 0
8 A 0 1
note the row names/indices.

You can try
library(dplyr)
inds <- df |> summarise(n = which(Value1 == 1 & c(Value2[2:n()] , 0) == 1))
df |> slice(unlist(Map(c , inds$n , inds$n + 1)))
data
ID Value1 Value2
1 A 1 0
2 A 0 1
3 A 1 0
4 A 0 1

Related

Select columns on match with vector and create ifelse condition with their content

I have a dataset with over several diseases, 0 indicating not having the disease and 1 having the disease.
To illustrate it with an example: I am interested in Diseases A and whether the people in the dataset have this diseases on its own or as the cause of another disease. Therefore I want to create a new variable "Type" with the values "NotDiseasedWithA", "Primary" and "Secondary". The diseases that can cause A are contained in a vector "SecondaryCauses":
SecondaryCauses = c("DiseaseB", "DiseaseD")
"NotDiseasedWithA" means that they do not have disease A.
"Primary" means that they have disease A but not any of the known diseases that can cause it.
"Secondary" means that they have disease A and a diseases that probably caused it.
Sample data
ID DiseaseA DiseaseB DiseaseC DiseaseD DiseaseE
1 0 1 0 0 0
2 1 0 0 0 1
3 1 0 1 1 0
4 1 0 1 1 1
5 0 0 0 0 0
My question is:
How do I select the columns I am interested in? I have more than 20 columns that are not ordered. Therefore I created the vector.
How do I create the condition based on the content of the diseases I am interested in?
I tried something like the following, but this did not work:
DF %>% mutate(Type = ifelse(DiseaseA == 0, "NotDiseasedWithA", ifelse(sum(names(DF) %in% SecondaryCauses) > 0, "Secondary", "Primary")))
So in the end I want to have this results:
ID DiseaseA DiseaseB DiseaseC DiseaseD DiseaseE Type
1 0 1 0 0 0 NotDiseasedWithA
2 1 0 0 0 1 Primary
3 1 0 1 1 0 Secondary
4 1 0 1 1 1 Secondary
5 0 0 0 0 0 NotDiseasedWithA
using data.table
df <- structure(list(ID = 1:5, DiseaseA = c(0L, 1L, 1L, 1L, 0L), DiseaseB = c(1L,
0L, 0L, 0L, 0L), DiseaseC = c(0L, 0L, 1L, 1L, 0L), DiseaseD = c(0L,
0L, 1L, 1L, 0L), DiseaseE = c(0L, 1L, 0L, 1L, 0L)), row.names = c(NA,
-5L), class = c("data.frame"))
library(data.table)
setDT(df) # make it a data.table
SecondaryCauses = c("DiseaseB", "DiseaseD")
df[DiseaseA == 0, Type := "NotDiseasedWithA"][DiseaseA == 1, Type := ifelse(rowSums(.SD) > 0, "Secondary", "Primary"), .SDcols = SecondaryCauses]
df
# ID DiseaseA DiseaseB DiseaseC DiseaseD DiseaseE Type
# 1: 1 0 1 0 0 0 NotDiseasedWithA
# 2: 2 1 0 0 0 1 Primary
# 3: 3 1 0 1 1 0 Secondary
# 4: 4 1 0 1 1 1 Secondary
# 5: 5 0 0 0 0 0 NotDiseasedWithA

In R, how do I spread a dummy variable value to everyone in a group (i.e., household) using something in the tidyverse?

I have data of individuals grouped into households. I'm trying to create a household-level dummy variable indicating a household with children. I've created a individual-level Child variable based on the observation's age. I'd like to "spread" this value, if it's a 1, to all members of the household.
The data looks like this:
HHID Child
1 0
1 1
1 0
2 0
2 1
3 0
3 0
3 0
I'd like the data frame like this:
HHID Child HH_child
1 0 1
1 1 1
1 0 1
2 0 1
2 1 1
3 0 0
3 0 0
3 0 0
I think it can be done using sqldf, but I'd like to do it in Tidyverse. Thanks!
Here is a tidyverse/dplyr solution:
library(dplyr)
df %>%
group_by(HHID) %>%
mutate(HH_child = if_else(any(Child == 1),1,0))
This gives us:
# A tibble: 8 x 3
HHID Child HH_child
<int> <int> <dbl>
1 1 0 1
2 1 1 1
3 1 0 1
4 2 0 1
5 2 1 1
6 3 0 0
7 3 0 0
8 3 0 0
Data:
structure(list(HHID = c(1L, 1L, 1L, 2L, 2L, 3L, 3L, 3L), Child = c(0L,
1L, 0L, 0L, 1L, 0L, 0L, 0L)), row.names = c(NA, -8L), .internal.selfref = <pointer: 0x0b952498>, class = c("tbl_df",
"tbl", "data.frame"))
Simply
library(dplyr)
df %>%
group_by(HHID) %>%
mutate(HH_child = max(Child))
We can also coerce to binary
library(dplyr)
df %>%
group_by(HHID) %>%
mutate(HH_child = +(1 %in% Child))
Or using base R
df$HH_child <- with(df, ave(Child == 1, HHID, FUN = any))

Refer to column name and row name within an apply statement in R

I have a dataframe in R which looks like the one below.
a b c d e f
0 1 1 0 0 0
1 1 1 1 0 1
0 0 0 1 0 1
1 0 0 1 0 1
1 1 1 0 0 0
The database is big, spanning over 100 columns and 5000 rows and contain all binaries (0's and 1's). I want to construct an overlap between each and every columns in R. Something like the one given below. This overlap dataframe will be a square matrix with equal number of rows and columns and that will be same as the number of columns in the 1st dataframe.
a b c d e f
a 3 2 2 2 0 2
b 2 3 3 3 0 1
c 2 3 3 1 0 1
d 2 3 1 3 0 3
e 0 0 0 0 0 0
f 2 1 1 3 0 3
Each cell of the second dataframe is populated by the number of cases where both row and column have 1 in the first dataframe.
I'm thinking of constructing a empty matrix like this:
df <- matrix(ncol = ncol(data), nrow = ncol(data))
colnames(df) <- names(data)
rownames(df) <- names(data)
.. and iterating over each cell of this matrix using an apply command reading the corresponding row name (say, x) and column name (say, y) and running a function like the one below.
summation <- function (x,y) (return (sum(data$x * data$y)))
The problem with is I can't find out the row name and column name while within an apply function. Any help will be appreciated.
Any more efficient way than what I'm thinking is more than welcome.
You are looking for crossprod
crossprod(as.matrix(df1))
# a b c d e f
#a 3 2 2 2 0 2
#b 2 3 3 1 0 1
#c 2 3 3 1 0 1
#d 2 1 1 3 0 3
#e 0 0 0 0 0 0
#f 2 1 1 3 0 3
data
df1 <- structure(list(a = c(0L, 1L, 0L, 1L, 1L), b = c(1L, 1L, 0L, 0L,
1L), c = c(1L, 1L, 0L, 0L, 1L), d = c(0L, 1L, 1L, 1L, 0L), e = c(0L,
0L, 0L, 0L, 0L), f = c(0L, 1L, 1L, 1L, 0L)), .Names = c("a",
"b", "c", "d", "e", "f"), class = "data.frame", row.names = c(NA,
-5L))

Segregating dataset and name each new dataset as per unique column names

I have a dataset(nm) as shown below:
nm
2_V2O 10_Kutti 14_DD 15_TT 16_DD 19_V2O 20_Kutti
0 1 1 0 0 1 0
1 1 1 1 1 0 0
0 1 0 1 0 0 1
0 1 1 0 1 0 0
Now I want to have multiple new datasets which got segregated as per their unique column names. All dataset names also must be created as per their column names as shown below:
Kutti
10_Kutti 20_Kutti
1 0
1 0
1 1
1 0
V2O
2_V2O 19_V2O
0 1
1 0
0 0
0 0
DD
14_DD 16_DD
1 0
1 1
0 0
1 1
TT
16_TT
0
1
0
1
I know this can be done using "select" function in dplyr but I need one dynamic programme which builds this automatically for any dataset.
We can split by the substring of the column names of 'nm'. Remove the prefix of the columnames until the _ with sub and use that to split the 'nm'.
lst <- split.default(nm, sub(".*_", "", names(nm)))
lst
#$DD
# 14_DD 16_DD
#1 1 0
#2 1 1
#3 0 0
#4 1 1
#$Kutti
# 10_Kutti 20_Kutti
#1 1 0
#2 1 0
#3 1 1
#4 1 0
#$TT
# 15_TT
#1 0
#2 1
#3 1
#4 0
#$V2O
# 2_V2O 19_V2O
#1 0 1
#2 1 0
#3 0 0
#4 0 0
It is better to keep the data.frames in a list. If we insist that it should be individual data.frame objects in the global environment (not recommended), use list2env
list2env(lst, envir = .GlobalEnv)
Now, just call
DD
data
nm <- structure(list(`2_V2O` = c(0L, 1L, 0L, 0L), `10_Kutti` = c(1L,
1L, 1L, 1L), `14_DD` = c(1L, 1L, 0L, 1L), `15_TT` = c(0L, 1L,
1L, 0L), `16_DD` = c(0L, 1L, 0L, 1L), `19_V2O` = c(1L, 0L, 0L,
0L), `20_Kutti` = c(0L, 0L, 1L, 0L)), .Names = c("2_V2O", "10_Kutti",
"14_DD", "15_TT", "16_DD", "19_V2O", "20_Kutti"), class = "data.frame",
row.names = c(NA, -4L))

R: add matrices based on row and column names

I have two matrices I want to sum based on their row and column names. The matrices will not necessarily have all rows and columns in common - some may be missing from either matrix.
For example, consider two matrices A and B:
A= B=
a b c d a c d e
v 1 1 1 0 v 0 0 0 1
w 1 1 0 1 w 0 0 1 0
x 1 0 1 1 y 0 1 0 0
y 0 1 1 1 z 1 0 0 0
Column e is missing from matrix A and column b is missing from matrix B.
Row z is missing from matrix A and row x is missing from matrix B.
The summed table I'm looking for is:
Sum=
a b c d e
v 1 1 1 0 1
w 1 1 0 2 0
x 1 0 1 1 na
y 0 1 2 1 0
z 1 na 0 0 0
The row and column ordering in the final matrix don't matter, as long as the matrix is complete, i.e. has all the data. Missing values don't have to be "Na", but could be "0" instead.
I'm not sure if there is a way to do this that doesn't involve for loops. Any help would be much appreciated.
My solution
I managed to do this easily by converting the matrices to dataframes, binding the dataframes by row and then casting the resulting dataframe back into a matrix. This looks like it works, but maybe someone could double check or let me know if there is a better way.
library(reshape2)
A_df=as.data.frame(as.table(A))
B_df=as.data.frame(as.table(B))
merged_df=rbind(A_df,B_df)
Summed_matrix=acast(merged_df, Var1 ~ Var2, sum)
merged_df looks like this:
Var1 Var2 Freq
1 v a 1
2 w a 1
3 x a 1
4 y a 0
5 v b 1
6 w b 1
etc...
May be you can try:
cAB <- union(colnames(A), colnames(B))
rAB <- union(rownames(A), rownames(B))
A1 <- matrix(0, ncol=length(cAB), nrow=length(rAB), dimnames=list(rAB, cAB))
B1 <- A1
indxA <- outer(rAB, cAB, FUN=paste) %in% outer(rownames(A), colnames(A), FUN=paste)
indxB <- outer(rAB, cAB, FUN=paste) %in% outer(rownames(B), colnames(B), FUN=paste)
A1[indxA] <- A
B1[indxB] <- B
A1+B1 #because it was mentioned to have `0` as missing values
# a b c d e
#v 1 1 1 0 1
#w 1 1 0 2 0
#x 1 0 1 1 0
#y 0 1 2 1 0
#z 1 0 0 0 0
If you want to get the NA as missing values
A1 <- matrix(NA, ncol=length(cAB), nrow=length(rAB), dimnames=list(rAB, cAB))
B1 <- A1
A1[indxA] <- A
B1[indxB] <- B
indxNA <- is.na(A1) & is.na(B1)
A1[is.na(A1)!= indxNA] <- 0
B1[is.na(B1)!= indxNA] <- 0
A1+B1
# a b c d e
#v 1 1 1 0 1
#w 1 1 0 2 0
#x 1 0 1 1 NA
#y 0 1 2 1 0
#z 1 NA 0 0 0
Or using reshape2
library(reshape2)
acast(rbind(melt(A), melt(B)), Var1~Var2, sum) #Inspired from the OP's idea
# a b c d e
#v 1 1 1 0 1
#w 1 1 0 2 0
#x 1 0 1 1 0
#y 0 1 2 1 0
#z 1 0 0 0 0
data
A <- structure(c(1L, 1L, 1L, 0L, 1L, 1L, 0L, 1L, 1L, 0L, 1L, 1L, 0L,
1L, 1L, 1L), .Dim = c(4L, 4L), .Dimnames = list(c("v", "w", "x",
"y"), c("a", "b", "c", "d")))
B <- structure(c(0L, 0L, 0L, 1L, 0L, 0L, 1L, 0L, 0L, 1L, 0L, 0L, 1L,
0L, 0L, 0L), .Dim = c(4L, 4L), .Dimnames = list(c("v", "w", "y",
"z"), c("a", "c", "d", "e")))

Resources