Extract rows by rank and condition in R

Viewed 74

I am trying to understand how to extract the top ten rows from a table based on the values in one numeric column, but only from rows that meet a condition applied to a second numeric column.
First about the data. I have a table listing, for a few thousand human genes, the difference in expression to a control (log_fold_change) and a p-value for this difference (p_value). The table looks something like this:

       log_fold_change   p_value
APOD         1.7388209 0.4820801
S100B       -1.1514299 0.5995658
CD63         0.6066951 0.4935413
PMEL        -1.4977796 0.1862176
MT2A        -0.9311173 0.8273733
S100A6      -0.4555436 0.6684667
TIMP1       -1.9464387 0.7942399
VIM         -0.4704482 0.1079436
PAEP         1.4787634 0.7237109
CSTB        -0.6386040 0.4112744

The data can be re-created using these commands (creates a table with data for n fictional genes):

n <- 50    
log_fold_change <- runif(n, -2.0, 2.0)
p_value <- runif(n, 0, 1.0)  
df <- data.frame(log_fold_change, p_value)
rownames(df) <- stringi::stri_paste(stringi::stri_rand_strings(n, 3, '[A-Z]'),stringi::stri_rand_strings(n, 1, '[1-9]'))

I have created a column for the labels (df$label <- NA), in which I plan to transfer the gene names that I want to label when I plot my graph. Which genes, you ask? I wish to, amongst genes with a positive log_fold_change, extract the ten genes with the smallest p_value.
I have already found a way to extract and label the 10 genes with the smallest p_value:

df$label[with(df, rank(p_value)) %in% c(1:10)] <- rownames(df)[with(df, rank(p_value)) %in% c(1:10)]

Now, how do I enforce the condition df$log_fold_change > 0 so that my ten genes with the smallest p_value are only selected from the genes with a positive log_fold_change? Any help would be really appreciated!

3 Answers

dplyr solution:

library(dplyr)

df %>%
  filter(log_fold_change > 0) %>%
  arrange(p_value) %>%
  head(10) %>%
  mutate(labels = rownames(.))

Output:

      A data.frame: 10 × 3
log_fold_change p_value label
   <dbl>       <dbl>    <chr>
0.9672121   0.01423086  MBT4
1.7139128   0.05044393  INQ3
0.4809652   0.10347463  ORW1
0.2949034   0.13493936  FVR2
0.9967781   0.17676960  JHP1
1.0813577   0.23599439  ITE9
1.0670343   0.25001069  ANQ4
1.5954271   0.26273176  XVL8
1.8803123   0.30065841  IQV6
0.3591054   0.46889062  JLD9

First you throw out all observations that are less or equal to 0 in the log_fold_change column with filter(log_fold_change > 0). Then arrange the p_value column in ascending order with arrange(p_value) and select the first 10 observations with head(10). Finally create the label columns with the names of the genes using mutate() . That gives you 10 observations with the lowest p_value where log_fold_change is positive.

You can order your data first, get the 10 values where log_fold_change > 0 and transfer the rownames to label column.

df <- df[order(df$log_fold_change), ]
inds <- which(df$log_fold_change > 0)[1:10]
df$label[inds] <- rownames(df)[inds]

This will keep your original df as it is and you will have label only in those 10 rows.

If you want only those 10 rows you can do :

df1 <- df[inds, ]

Using base R this could be achieved like so:

  1. Split your df by log_fold_change > 0.
  2. Set the labels separatly for each split
  3. Bind back together
n <- 50    
log_fold_change <- runif(n, -2.0, 2.0)
p_value <- runif(n, 0, 1.0)  
df <- data.frame(log_fold_change, p_value)
rownames(df) <- stringi::stri_paste(stringi::stri_rand_strings(n, 3, '[A-Z]'),stringi::stri_rand_strings(n, 1, '[1-9]'))

df_split <- split(df, df$log_fold_change > 0) 
df_split[["FALSE"]]$label <- NA
df_split[["TRUE"]]$label <- with(df_split[["TRUE"]], rank(-p_value))
df_split[["TRUE"]]$label <- ifelse(df_split[["TRUE"]]$label %in% 1:10, row.names(df_split[["TRUE"]]), NA)
df <- rbind(df_split[["FALSE"]], df_split[["TRUE"]])

df[!is.na(df$label),]
#>      log_fold_change   p_value label
#> IPQ1      1.46479105 0.9928132  IPQ1
#> GDA3      0.26974526 0.9696272  GDA3
#> CHU3      0.31643010 0.7094205  CHU3
#> IDE1      0.87836008 0.7152144  IDE1
#> KQJ9      1.12647503 0.7854047  KQJ9
#> FQM8      1.99924827 0.8463336  FQM8
#> RPO1      0.10874876 0.7279679  RPO1
#> TOA9      1.88312825 0.7143039  TOA9
#> EXG7      1.82497903 0.8082063  EXG7
#> RGE9      0.02894958 0.7685504  RGE9

Created on 2020-10-03 by the reprex package (v0.3.0)

Related