Code Review
One change I would make to your solution is to directly modify the row names as below (instead of
modifying gene
and then setting the row names equal to gene
).
for (i in 1:nrow(df)){
if (rownames(df)[i] %in% annotation$gene) {
rownames(df)[i] = annotation$name[which(annotation$gene == rownames(df)[i])]
}
}
df$gene <- NULL
This version is quite similar to yours, but iterates over row numbers (1-3 in this case) instead of row names. This allows direct modification of the row names and makes the code slightly more concise. This version doesn't use gene
at all.
This is how I would modify your existing solution, but overall I'd caution against using a for
loop at all. for
loops can be slow since they iterate over every element (and your data set has 10^5 rows). Below is another approach using the dplyr
library.
Another Approach
library(dplyr)
library(tibble)
df <- left_join(df, annotation, by = "gene") %>% # Join "annotation" and "df"
mutate(gene = if_else(is.na(name), gene, name)) %>% # Convert "gene" to "name" when "name" is valid
column_to_rownames(var = "gene") %>% # Set row names to "gene"
select(-name) # Remove "name"
This syntax may look unfamiliar but it's doing 3 main things:
- left-joining
df
andannotation
by thegene
field. It has to be a left-join so all rows ofdf
are preserved! - changing the value of
gene
toname
only whenname
is non-null - setting the row names equal to
gene
The pipe operator %>%
passes the data through these operations without needing to repeatedly type df <-
. These are all vectorized operations and so are faster than a for
loop, especially for large data sets.
dplyr
syntax can be intimidating at first, but it's an extremely popular (and I believe intuitive) paradigm to manipulate data. Here is a handy intro on dplyr
syntax.