Replace a particular value with a value from the row above - r

I have a large table which has a few columns that have "-" in them. I want to replace "-" with the value from the row above in the same column
library(tidyverse)
# This is the df I have
df <- data.frame(stringsAsFactors=FALSE,
my = c("a", "a", "a", "-", "b", "b", "b", "-", "c", "c", "c", "-"),
bad = c("d", "d", "d", "-", "e", "e", "e", "-", "f", "f", "f", "-"),
table = c("g", "g", "g", "-", "h", "h", "h", "-", "i", "i", "i", "-")
)
# This is the desired output:
output_df <- data.frame(stringsAsFactors=FALSE,
my = c("a", "a", "a", "a", "b", "b", "b", "b", "c", "c", "c", "c"),
bad = c("d", "d", "d", "d", "e", "e", "e", "e", "f", "f", "f", "f"),
table = c("g", "g", "g", "g", "h", "h", "h", "h", "i", "i", "i", "i")
)
# What I have tried unsuccessfully:
df %>%
mutate_at(c("my", "bad", "table"), .funs = str_replace("-", NA))
I'm a bit stumped with this one.....any ideas?

An option is fill after changing the - to NA
library(tidyverse)
df %>%
mutate_all(na_if, "-") %>%
fill(my, bad, table)
# orif there are many columns
# fill(!!! rlang::syms(names(.)))
# or as H1 suggested
# fill(everything())
# my bad table
#1 a d g
#2 a d g
#3 a d g
#4 a d g
#5 b e h
#6 b e h
#7 b e h
#8 b e h
#9 c f i
#10 c f i
#11 c f i
#12 c f i

Related

Converting multiple columns to factors and releveling with mutate(across)

dat <- data.frame(Comp1Letter = c("A", "B", "D", "F", "U", "A*", "B", "C"),
Comp2Letter = c("B", "C", "E", "U", "A", "C", "A*", "E"),
Comp3Letter = c("D", "A", "C", "D", "F", "D", "C", "A"))
GradeLevels <- c("A*", "A", "B", "C", "D", "E", "F", "G", "U")
I have a dataframe that looks something like the above (but with many other columns I don't want to change).
The columns I am interested in changing contains lists of letter grades, but are currently character vectors and not in the right order.
I need to convert each of these columns into factors with the correct order. I've been able to get this to work using the code below:
factordat <-
dat %>%
mutate(Comp1Letter = factor(Comp1Letter, levels = GradeLevels)) %>%
mutate(Comp2Letter = factor(Comp2Letter, levels = GradeLevels)) %>%
mutate(Comp3Letter = factor(Comp3Letter, levels = GradeLevels))
However this is super verbose and chews up a lot of space.
Looking at some other questions, I've tried to use a combination of mutate() and across(), as seen below:
factordat <-
dat %>%
mutate(across(c(Comp1Letter, Comp2Letter, Comp3Letter) , factor(levels = GradeLetters)))
However when I do this the vectors remain character vectors.
Could someone please tell me what I'm doing wrong or offer another option?
You can do across as an anonymous function like this:
dat <- data.frame(Comp1Letter = c("A", "B", "D", "F", "U", "A*", "B", "C"),
Comp2Letter = c("B", "C", "E", "U", "A", "C", "A*", "E"),
Comp3Letter = c("D", "A", "C", "D", "F", "D", "C", "A"))
GradeLevels <- c("A*", "A", "B", "C", "D", "E", "F", "G", "U")
dat %>%
tibble::as_tibble() %>%
dplyr::mutate(dplyr::across(c(Comp1Letter, Comp2Letter, Comp3Letter) , ~forcats::parse_factor(., levels = GradeLevels)))
# # A tibble: 8 × 3
# Comp1Letter Comp2Letter Comp3Letter
# <fct> <fct> <fct>
# 1 A B D
# 2 B C A
# 3 D E C
# 4 F U D
# 5 U A F
# 6 A* C D
# 7 B A* C
# 8 C E A
You were close, all that was left to be done was make the factor function anonymous. That can be done either with ~ and . in tidyverse or function(x) and x in base R.

How would I identify which columns and rows match between two data matrices?

I have two data matrices of different dimensions stored as objects in R (I am using Rstudio with R v4.0.2 in Windows 10):
m1 = 1 column x 44 rows (this is a list of names with no spaces).
m2 = 500,000 columns x 164 rows (this contains a string of characters, the first row being a list of names).
I want to check how many (and which) of the rows of m1 are found in m2 (meaning it will be anywhere between 0 and 44). The end goal is that I have 4000 different matrices that will substitute the place of m2, and I need to see the extent of missing entries (found in m1) in all of the m2s (i.e., I am looking at the extent of missing data of those 44 names).
I am still a beginner to R, so apologies if my description is a bit off.
I tried storing each matrix, saved as CSV files, as such:
m1 <- read.csv("names-file.csv")
m2 <- read.csv("data-file.csv")
and tried to use the row.match function in the prodlim package, and ran row.match(m1, m2) but only got numeric values. I am looking to see just a number of how many of the names from m1 (first column) are found in m2 (first column), which values those are, and what the percentage would be (x out of 44).
As an example:
m1 =
Tom
Harry
Cindy
Megan
Jack
`
m2 =
Tom XXXXXXXXXXXX----XXXXXXXX
Stephanie XXXXXXXXXXXXXXXX----XXXX
Megan XXXXXXXXXXXXXXXXXXXXXXXX
Ryan XXXXXXXXXXXXXXXXXXXXXX-X
David XXXXXX---XXXXXXXXXXXXXXX
Josh XXXXXXXXXXXXXXXXXXXXXXXX
In the m2 matrix, each name is column 1, and the each subsequent X (which represents either an A, T, C, or G) are the subsequent columns (so some columns have an A, T, C, or G, or a "-"). I am looking to write a code that would see how many of the names from m1 and found in m2 (and conversely, how much data is missing from m2 as a percentage). In this case, the desired outputs would be:
2
Tom
Megan
60%
Here are my specific datafile using dput() (please let me know if I am using dput() correctly):
m1:
structure(list(V1 = c("Taxon1", "Taxon2", "Taxon3", "Taxon4",
"Taxon5", "Taxon6", "Taxon7", "Taxon8")), class = "data.frame", row.names = c(NA,
-8L))
m2:
structure(list(V1 = c("Taxon1", "Taxon3", "Taxon4", "Taxon6",
"Taxon7", "Taxon9", "Taxon10", "Taxon11", "Taxon12", "Taxon13",
"Taxon14", "Taxon15", "Taxon16", "Taxon17", "Taxon18", "Taxon19",
"Taxon20", "Taxon21", "Taxon22", "Taxon23", "Taxon24", "Taxon25",
"Taxon26", "Taxon27", "Taxon28", "Taxon29", "Taxon30"), V2 = c("A",
"A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A",
"A", "A", "A", "A", "A", "A", "C", "C", "C", "C", "C", "C", "C"
), V3 = c("G", "G", "G", "G", "G", "C", "C", "G", "G", "G", "G",
"G", "G", "G", "G", "G", "G", "G", "G", "G", "G", "G", "G", "G",
"G", "G", "G"), V4 = c("C", "C", "C", "C", "C", "T", "G", "C",
"C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C",
"C", "C", "C", "C", "C", "C"), V5 = c("T", "T", "G", "T", "G",
"G", "G", "T", "T", "T", "T", "T", "T", "T", "T", "T", "T", "T",
"T", "T", "T", "T", "T", "T", "T", "T", "T"), V6 = c("G", "G",
"C", "G", "C", "C", "C", "G", "G", "G", "G", "G", "G", "G", "G",
"G", "G", "G", "G", "G", "G", "G", "G", "G", "G", "G", "G"),
V7 = c("C", "C", "A", "C", "A", "A", "A", "C", "C", "C",
"C", "C", "C", "C", "C", "C", "G", "G", "G", "G", "G", "G",
"G", "G", "G", "G", "G"), V8 = c("T", "T", "A", "T", "A",
"A", "A", "T", "T", "T", "T", "T", "T", "T", "T", "T", "T",
"T", "T", "T", "T", "T", "T", "T", "T", "T", "T"), V9 = c("A",
"A", "A", "A", "A", "T", "T", "A", "A", "A", "A", "A", "A",
"A", "A", "A", "A", "T", "T", "T", "T", "T", "T", "T", "T",
"T", "T")), class = "data.frame", row.names = c(NA, -27L))
Thank you!
You might want to have a look at the %in% operator in R. According to your question, you might want something like this:
m1[,1] %in% m2[,1]
#[1] TRUE FALSE TRUE TRUE FALSE TRUE TRUE FALSE
You can then pair it with functions such as mean or sum which will help you to find the percentage as required:
sum(m1[,1] %in% m2[,1])
#[1] 5
mean(m1[,1] %in% m2[,1])
#[1] 0.625
EDIT: As required by the OP in the comments of this post, there are various methods for that, my personal favourite being the which function:
m1[which(m1[,1] %in% m2[,1]),]
#[1] "Taxon1" "Taxon3" "Taxon4" "Taxon6" "Taxon7"
m1[which(!(m1[,1] %in% m2[,1])),]
#[1] "Taxon2" "Taxon5" "Taxon8"
Again, this is only one method, out of many (I can count 3 right now...), so I suggest you to explore the other options...
To get common names in both the dataframes you may use intersect, to calculate missing percentage you can use %in% with mean
common_names <- intersect(m1$V1, m2$V1)
missing_percentage_in_m1 <- mean(!m1$V1 %in% m2$V1) * 100
missing_percentage_in_m2 <- mean(!m2$V1 %in% m1$V1) * 100
common_names
#[1] "Taxon1" "Taxon3" "Taxon4" "Taxon6" "Taxon7"
missing_percentage_in_m1
#[1] 37.5
missing_percentage_in_m2
#[1] 81.48148
This code will get result like this
2
Tom
Megan
60%
1.how many of the names from m1 and found in m2
m1 <- t(m1)
res1 <-m2 %>%
rowwise %>%
mutate(n = m1 %in% c_across(V1:V9) %>% sum)
res1
# A tibble: 27 x 10
# Rowwise:
V1 V2 V3 V4 V5 V6 V7 V8 V9 n
<chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <int>
1 Taxon1 A G C T G C T A 1
2 Taxon3 A G C T G C T A 1
3 Taxon4 A G C G C A A A 1
4 Taxon6 A G C T G C T A 1
5 Taxon7 A G C G C A A A 1
6 Taxon9 A C T G C A A T 0
7 Taxon10 A C G G C A A T 0
8 Taxon11 A G C T G C T A 0
9 Taxon12 A G C T G C T A 0
10 Taxon13 A G C T G C T A 0
# ... with 17 more rows
res1 %>% select(n) %>% sum
[1] 5
res2 <-res1 %>%
filter(n >0) %>%
pull(V1) %>%
unique
res2
[1] "Taxon1" "Taxon3" "Taxon4" "Taxon6" "Taxon7"
2.how much data is missing from m2 as a percentage
res3 <- res2 %>% length
1 - res3 / length(unique(m2$V1))
[1] 0.8148148

Choose a set of markers to distinguish individuals

I have some genotype data from 8 markers and 20 individuals. I would like to select 5 markers from the 8 markers, which can form a unique genotype pattern for each individual. The purpose is to select as few as possible markers to distinguish the 20 individuals.
I know that I need to select 5 columns out of 8, and then compare each row. If we find duplicated rows, then we need to re-select another 5 columns, until we find no duplicated rows.
But I don't know how I can translate it into R. Could somebody help? Thanks!
sample data
Indiv MN1 MN2 MN3 MN4 MN5 MN6 MN7 MN8
1 A C C A C G A T
2 A C T A T A A T
3 A C T G C A A C
4 A C T G C G G C
5 A T C G C A A C
6 A T C G C A G C
7 A T T A T A A T
8 A T T A T A G T
9 A T T A T G G C
10 G C C A C A A C
11 G C C A C G A T
12 G C C G C G G T
13 G C C G T G G T
14 G C T G C G A T
15 G C T G T A G C
16 G T C A T A G T
17 G T C G T A A C
18 G T T A C G G T
19 G T T G T G G T
Impossible. Assume that we can't change the order of markers. You need at least 6 markers to distinguish the individuals. Consider this function (brute-force solution).
distinct_combn <- function(df, m) {
out <- combn(df, m, function(x) {
if (nrow(unique(x)) == nrow(x)) names(x) else character(0L)
}, simplify = FALSE)
out[lengths(out) > 0L]
}
Then we can see that
> distinct_combn(df[, -1L], 5)
list()
> distinct_combn(df[, -1L], 6)
[[1]]
[1] "MN1" "MN2" "MN3" "MN5" "MN6" "MN7"
[[2]]
[1] "MN1" "MN2" "MN3" "MN5" "MN7" "MN8"
[[3]]
[1] "MN1" "MN2" "MN4" "MN5" "MN6" "MN7"
[[4]]
[1] "MN1" "MN2" "MN4" "MN5" "MN7" "MN8"
Data I used
> df
Indiv MN1 MN2 MN3 MN4 MN5 MN6 MN7 MN8
1 1 A C C A C G A T
2 2 A C T A T A A T
3 3 A C T G C A A C
4 4 A C T G C G G C
5 5 A T C G C A A C
6 6 A T C G C A G C
7 7 A T T A T A A T
8 8 A T T A T A G T
9 9 A T T A T G G C
10 10 G C C A C A A C
11 11 G C C A C G A T
12 12 G C C G C G G T
13 13 G C C G T G G T
14 14 G C T G C G A T
15 15 G C T G T A G C
16 16 G T C A T A G T
17 17 G T C G T A A C
18 18 G T T A C G G T
19 19 G T T G T G G T
> dput(df)
structure(list(Indiv = 1:19, MN1 = c("A", "A", "A", "A", "A",
"A", "A", "A", "A", "G", "G", "G", "G", "G", "G", "G", "G", "G",
"G"), MN2 = c("C", "C", "C", "C", "T", "T", "T", "T", "T", "C",
"C", "C", "C", "C", "C", "T", "T", "T", "T"), MN3 = c("C", "T",
"T", "T", "C", "C", "T", "T", "T", "C", "C", "C", "C", "T", "T",
"C", "C", "T", "T"), MN4 = c("A", "A", "G", "G", "G", "G", "A",
"A", "A", "A", "A", "G", "G", "G", "G", "A", "G", "A", "G"),
MN5 = c("C", "T", "C", "C", "C", "C", "T", "T", "T", "C",
"C", "C", "T", "C", "T", "T", "T", "C", "T"), MN6 = c("G",
"A", "A", "G", "A", "A", "A", "A", "G", "A", "G", "G", "G",
"G", "A", "A", "A", "G", "G"), MN7 = c("A", "A", "A", "G",
"A", "G", "A", "G", "G", "A", "A", "G", "G", "A", "G", "G",
"A", "G", "G"), MN8 = c("T", "T", "C", "C", "C", "C", "T",
"T", "C", "C", "T", "T", "T", "T", "C", "T", "C", "T", "T"
)), class = "data.frame", row.names = c(NA, -19L))

Loop through the columns to search for multiple variables in R

Sorry, that this is a follow-up question. I am trying to count how many 'S' and 'T' appears in each column as 'downstream' from 1 to 10 rows and then as 'upstream' from 15 to 25.
ST <- data.frame(scale = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0),
aa = c('A','C','D','E','F','G','H','I','K','L','M','N','P','Q','R','S','T','V','W','Y'))
#input (example)
V1 V2 V3 V4 V5
1 C D E R N
2 C A M K P
3 V T Q Q E
4 A T S S S
5 C D E R N
6 C A M K P
7 V T Q Q E
8 A T S S S
9 R V D S A
10 W R H I C
11 S N I P T
12 Q A S D E
13 C D E R N
14 C A M K P
15 V T Q Q E
16 A T S S S
17 C D E R N
18 C A M K P
19 V T Q Q E
20 A T S S S
21 R V D S A
22 W R H I C
23 S N I P T
24 G A D S S
25 N T T S A
When I had a data frame with 'S' only, the script below worked but with 'ST', it doesn't. Could someone tell me why? Of course, I could get 'S' and 'T' separately and then add it later but is there a way to do it through this single data frame 'ST'?
#sum values from positions 1 to 10 and then from 15 to 25 works well for 1 letter only
count_aa <- df_trial %>%
summarise(across(everything(), ~ c(sum(.[1:10] == 'T'), sum(.[15:25] == 'T')))) %>%
mutate(categ = c('upstream', 'downstream'), .before = 1)
#view(count_aa)
df_count_aa<- as.data.frame(t(count_aa))
#view(df_count_aa)
We can use %in% instead of == when there are more than one element to compare
library(dplyr)
df_trial %>%
summarise(across(everything(), ~
c(sum(.[1:10] %in% c('S', 'T')),
sum(.[15:25] %in% c('S', 'T'))))) %>%
mutate(categ = c('upstream', 'downstream'), .before = 1)
-output
# categ V1 V2 V3 V4 V5
#1 upstream 0 4 2 3 2
#2 downstream 1 5 3 5 4
The == is doing elementwise comparison. If we do the == with more than one element as == c("S", "T"), then it does a recycling of the vector elements to the entire length of the column resulting i.e. 'S' gets compared to the first element of the colum, 'T' to second element, 'S' again to 3rd element and so on... i.e. the comparison would be based on position
In base R we can do colSums
colSums(df_trial == 'S') + colSums(df_trial == 'T')
In base R, you can do this sapply :
data.frame(categ = c('upstream', 'downstream'),
sapply(df_trial, function(x)
c(sum(x[1:10] %in% c('S', 'T')), sum(x[15:25] %in% c('S', 'T')))))
# categ V1 V2 V3 V4 V5
#1 upstream 0 4 2 3 2
#2 downstream 1 5 3 5 4
Using base R
> rbind(downstream = sapply(df[1:10,], function(x) sum(grepl('[ST]',x))),
+ upstream = sapply(df[15:25,], function(x) sum(grepl('[ST]',x))))
V1 V2 V3 V4 V5
downstream 0 4 2 3 2
upstream 1 5 3 5 4
>
Data Used:
> dput(df)
structure(list(V1 = c("C", "C", "V", "A", "C", "C", "V", "A",
"R", "W", "S", "Q", "C", "C", "V", "A", "C", "C", "V", "A", "R",
"W", "S", "G", "N"), V2 = c("D", "A", "T", "T", "D", "A", "T",
"T", "V", "R", "N", "A", "D", "A", "T", "T", "D", "A", "T", "T",
"V", "R", "N", "A", "T"), V3 = c("E", "M", "Q", "S", "E", "M",
"Q", "S", "D", "H", "I", "S", "E", "M", "Q", "S", "E", "M", "Q",
"S", "D", "H", "I", "D", "T"), V4 = c("R", "K", "Q", "S", "R",
"K", "Q", "S", "S", "I", "P", "D", "R", "K", "Q", "S", "R", "K",
"Q", "S", "S", "I", "P", "S", "S"), V5 = c("N", "P", "E", "S",
"N", "P", "E", "S", "A", "C", "T", "E", "N", "P", "E", "S", "N",
"P", "E", "S", "A", "C", "T", "S", "A")), row.names = c(NA, -25L
), class = c("tbl_df", "tbl", "data.frame"))
>

Replace values across multiple varibles in R

I have a dataframe with 82 variables. Many of the variables contain alphabetic letters, which I want to change into a set of numbers. I can do this column-by-column, number-by-number using the code below:
library(tibble)
mydf <- tribble(~Var1, ~Var2.a, ~Var3.a, ~Var4.a,
"A", "b", "b", "d",
"B", "w", NA, "w",
"C", "g", "k", "b",
"D", "k", NA, "j")
newdf <- mydf %>%
mutate(Var2.a = ifelse(Var2.a %in% c("m", "p", "w", "h", "n"), 1, Var2.a),
Var2.a = ifelse(Var2.a %in% c("k", "b", "g", "j", "f", "d"), 2, Var2.a),
Var3.a = ifelse(Var3.a %in% c("m", "p", "w", "h", "n"), 1, Var3.a),
Var3.a = ifelse(Var3.a %in% c("k", "b", "g", "j", "f", "d"), 2, Var3.a),
Var4.a = ifelse(Var4.a %in% c("m", "p", "w", "h", "n"), 1, Var4.a),
Var4.a = ifelse(Var4.a %in% c("k", "b", "g", "j", "f", "d"), 2, Var4.a))
But this will take a lot of time for the 70+ columns I need to change!
All the variables of interest have a matching letter combination in the variable name (".a" in the example data), so I should be able to use an ifelse statement on these columns using contains(). However I can't work out how to do this!
I have looked at this answer, which I think is getting me close, but I can't work out how to embed an if-statement into it:
newdf <- mydf %>%
mutate_at(vars[2:4] = ifelse(vars %in% c("m", "p", "w", "h", "n"), 1, vars)
But I get the error Error in vars[2:4] : object of type 'closure' is not subsettable. I think the brackets are wrong here, and probably also the use of vars!
Try this example:
# custom function, I prefer case_when (we could use nested if_else if needed.)
foo <- function(x){
case_when(
x %in% c("m", "p", "w", "h", "n") ~ 1L,
x %in% c("k", "b", "g", "j", "f", "d") ~ 2L,
TRUE ~ NA_integer_)
}
mydf %>%
mutate_at(vars(Var2.a:Var4.a), foo)
# # A tibble: 4 x 4
# Var1 Var2.a Var3.a Var4.a
# <chr> <int> <int> <int>
# 1 A 2 2 2
# 2 B 1 NA 1
# 3 C 2 2 2
# 4 D 2 NA 2

Resources