Here we show how to use limorhyde2 to quantify
rhythmicity in data from one condition. The data are based on mouse
liver samples from the circadian gene expression
atlas in mammals (GSE54650).
library('data.table')
library('ggplot2')
library('limorhyde2')
library('qs')
# doParallel::registerDoParallel() # register a parallel backend to minimize runtime
theme_set(theme_bw())The expression data are in a matrix with one row per gene and one column per sample. The metadata are in a table with one row per sample. To save time and space, the expression data include only a subset of genes.
y = GSE54650$y
y[1:5, 1:5]
#>           GSM1321182 GSM1321183 GSM1321184 GSM1321185 GSM1321186
#> 13088      10.209968  10.503358   9.714828   9.477728  10.134381
#> 13170       7.871989   7.281691   6.946495   6.266409   7.150140
#> 13869       6.851685   7.406234   7.781294   7.924764   8.089538
#> 100009600   5.055931   5.199767   5.121511   5.155912   5.219873
#> 100418030   6.174130   6.547075   6.521593   6.413817   6.451957
metadata = GSE54650$metadata
metadata
#>         sample tissue  time
#>         <char> <char> <int>
#>  1: GSM1321182  liver    18
#>  2: GSM1321183  liver    20
#>  3: GSM1321184  liver    22
#>  4: GSM1321185  liver    24
#>  5: GSM1321186  liver    26
#>  6: GSM1321187  liver    28
#>  7: GSM1321188  liver    30
#>  8: GSM1321189  liver    32
#>  9: GSM1321190  liver    34
#> 10: GSM1321191  liver    36
#> 11: GSM1321192  liver    38
#> 12: GSM1321193  liver    40
#> 13: GSM1321194  liver    42
#> 14: GSM1321195  liver    44
#> 15: GSM1321196  liver    46
#> 16: GSM1321197  liver    48
#> 17: GSM1321198  liver    50
#> 18: GSM1321199  liver    52
#> 19: GSM1321200  liver    54
#> 20: GSM1321201  liver    56
#> 21: GSM1321202  liver    58
#> 22: GSM1321203  liver    60
#> 23: GSM1321204  liver    62
#> 24: GSM1321205  liver    64
#>         sample tissue  timeThe first step is to fit a series of linear models based on periodic
splines for each genomic feature, in this case each gene, using limma.
getModelFit() takes several arguments besides the
expression data and metadata, but here we just use the defaults. For
example, the data are from one condition, so we leave
condColname as NULL. Also, the expression data
are from microarrays and already log-transformed, so we leave
method as 'trend'.
fit = getModelFit(y, metadata)The next step is obtain posterior estimates of the model coefficients using multivariate adaptive shrinkage (mashr), which learns patterns in the data and accounts for noise in the original fits.
fit = getPosteriorFit(fit)We can now use the posterior fits to compute rhythm statistics, i.e. properties of the curve, for each gene.
rhyStats = getRhythmStats(fit)We can examine the distributions of the statistics in various ways, such as ranking genes by peak-to-trough amplitude (no p-values necessary) or plotting peak-to-trough amplitude vs. peak phase.
print(rhyStats[order(-peak_trough_amp)], nrows = 10L)
#>       feature peak_phase peak_value trough_phase trough_value peak_trough_amp
#>        <char>      <num>      <num>        <num>        <num>           <num>
#>  1:     13170  8.6775145  10.290602    22.346123     6.765579     3.525022206
#>  2:    266645 19.5106275   9.800471     8.768400     8.237576     1.562895177
#>  3:     13088 18.6818895  10.412693     9.015083     8.883997     1.528695535
#>  4:     13869  8.3932348   8.524375    18.785793     7.207670     1.316705132
#>  5:     68396 20.3289764   9.678209     8.714920     8.809740     0.868468524
#> ---                                                                          
#> 46: 100009600  2.4406727   5.125981    18.711851     5.124404     0.001577536
#> 47:     12613  0.4067764   5.049751     9.355932     5.048220     0.001530589
#> 48:     14616  0.4067829   5.420731    17.491505     5.419337     0.001394137
#> 49:     23801 16.2711757   5.371221     9.355932     5.370178     0.001043270
#> 50: 115485688 19.9322039   4.235861    11.796608     4.235586     0.000275261
#>        mesor
#>        <num>
#>  1: 8.478849
#>  2: 9.092471
#>  3: 9.761742
#>  4: 7.853238
#>  5: 9.283828
#> ---         
#> 46: 5.125043
#> 47: 5.048960
#> 48: 5.420226
#> 49: 5.370578
#> 50: 4.235683
ggplot(rhyStats) +
  geom_point(aes(x = peak_phase, y = peak_trough_amp), alpha = 0.2) +
  xlab('Peak phase (h)') +
  ylab('Peak-to-trough amplitude (norm.)') +
  scale_x_continuous(breaks = seq(0, 24, 4))We can also compute the expected measurements for one or more genes at one or more time-points, which correspond to the fitted curves. Here we plot the posterior fits and observed expression for three of the top rhythmic genes (converting from gene id to gene symbol).
genes = data.table(
  id = c('13088', '13170', '13869'),
  symbol = c('Cyp2b10', 'Dbp', 'Erbb4'))
measFit = getExpectedMeas(fit, times = seq(0, 24, 0.5), features = genes$id)
measFit[genes, symbol := i.symbol, on = .(feature = id)]
print(measFit, nrows = 10L)
#>       time feature     value  symbol
#>      <num>  <char>     <num>  <char>
#>   1:   0.0   13088 10.146744 Cyp2b10
#>   2:   0.0   13170  6.949242     Dbp
#>   3:   0.0   13869  7.631226   Erbb4
#>   4:   0.5   13088 10.108186 Cyp2b10
#>   5:   0.5   13170  7.081859     Dbp
#>  ---                                
#> 143:  23.5   13170  6.853361     Dbp
#> 144:  23.5   13869  7.570816   Erbb4
#> 145:  24.0   13088 10.146744 Cyp2b10
#> 146:  24.0   13170  6.949242     Dbp
#> 147:  24.0   13869  7.631226   Erbb4Next we combine the observed expression data and metadata. The curves
show how limorhyde2 is able to fit non-sinusoidal
rhythms.
measObs = mergeMeasMeta(y, metadata, features = genes$id)
measObs[genes, symbol := i.symbol, on = .(feature = id)]
print(measObs, nrows = 10L)
#> Key: <sample>
#>         sample tissue  time feature      meas  symbol
#>         <char> <char> <int>  <char>     <num>  <char>
#>  1: GSM1321182  liver    18   13088 10.209968 Cyp2b10
#>  2: GSM1321182  liver    18   13170  7.871989     Dbp
#>  3: GSM1321182  liver    18   13869  6.851685   Erbb4
#>  4: GSM1321183  liver    20   13088 10.503358 Cyp2b10
#>  5: GSM1321183  liver    20   13170  7.281691     Dbp
#> ---                                                  
#> 68: GSM1321204  liver    62   13170  8.656560     Dbp
#> 69: GSM1321204  liver    62   13869  6.970524   Erbb4
#> 70: GSM1321205  liver    64   13088 10.352095 Cyp2b10
#> 71: GSM1321205  liver    64   13170  8.157525     Dbp
#> 72: GSM1321205  liver    64   13869  7.048457   Erbb4
ggplot() +
  facet_wrap(vars(symbol), scales = 'free_y', nrow = 1) +
  geom_line(aes(x = time, y = value), data = measFit) +
  geom_point(aes(x = time %% 24, y = meas), shape = 21, size = 1.5,
             data = measObs) +
  labs(x = 'Circadian time (h)', y = 'Expression (norm.)') +
  scale_x_continuous(breaks = seq(0, 24, 4))