Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

https://github.com/junting-ren/hair_simulation
05 March 2025, 22:48:23 UTC
  • Code
  • Branches (2)
  • Releases (0)
  • Visits
    • Branches
    • Releases
    • HEAD
    • refs/heads/add-license-1
    • refs/heads/master
    No releases to show
  • f16e9d3
  • /
  • hair.Rmd
Raw File Download
Take a new snapshot of a software origin

If the archived software origin currently browsed is not synchronized with its upstream version (for instance when new commits have been issued), you can explicitly request Software Heritage to take a new snapshot of it.

Use the form below to proceed. Once a request has been submitted and accepted, it will be processed as soon as possible. You can then check its processing state by visiting this dedicated page.
swh spinner

Processing "take a new snapshot" request ...

Permalinks

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
  • revision
  • snapshot
origin badgecontent badge Iframe embedding
swh:1:cnt:7191825b87185c1869b43b739829f16d3a8f9f63
origin badgedirectory badge Iframe embedding
swh:1:dir:f16e9d37df5daaefb7c5c37d5abdaa84bca623d6
origin badgerevision badge
swh:1:rev:3a19705969bfca7edc98651c1dd973ca7ae3b23d
origin badgesnapshot badge
swh:1:snp:49785cbcfa2a5484bdfb4fed78690b30a9a423e9
Citations

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
  • revision
  • snapshot
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Tip revision: 3a19705969bfca7edc98651c1dd973ca7ae3b23d authored by Junting on 15 June 2021, 05:09:06 UTC
Update README.md
Tip revision: 3a19705
hair.Rmd
---
title: "Quantitative Mapping of Human Hair Greying and Reversal in Relation to Life Stress"
output: 
  flexdashboard::flex_dashboard:
    orientation: columns
    vertical_layout: fill
    social: menu
    source_code: embed
  html_document:
    df_print: paged
runtime: shiny
---

```{r setup, include=FALSE}
library(flexdashboard)
library(mvtnorm)
library(tidyverse)
library(zoo)
library(plotly)
library(DT)
# Creates ? Help Popups
helpPopup <- function(title) {
  tagList(
    tags$a(
      href='#', title=title, 
      `data-toggle`='popover', `data-animation`=TRUE,
      `data-placement`='right', `data-trigger`='hover',
      
      tags$i(class='glyphicon glyphicon-question-sign')
    )
  )
} # END helpPopup

#function for generating the simulation data
sim_data_generate = function(beta1 = 1,beta2 = 1,sigma1 = 10,sigma2 = 11,
                             sigma3 = 12,sigmae = 1,rho12 = 0.5,
                             rho13 = 0.55,rho23 = 0.6,step = 0.1,
                             window_width = 5,stress_start = c(20,20),
                             stress_length = c(0.1,10), stress_strength = c(100,10),
                             thres = 1900,n=100,
                             seed_num = 2019){
  set.seed(seed_num)
  #Correlation matrix
  R <- matrix(numeric(3*3), nrow = 3)   # will be the correlation matrix
  diag(R) <- 1                        # set diagonal to 1
  R[upper.tri(R)] <- c(rho12, rho13, rho23)       # fill in to upper right
  R[lower.tri(R)] <- c(rho12, rho13, rho23)       # fill in to lower left
  #Covairance matrix
  G = diag(c(sigma1,sigma2,sigma3)) %*% R %*% diag(c(sigma1,sigma2,sigma3))
  #Stress data for one hair
  stress_data = tibble(age = seq(from = step, to = 100, by = step))
  for(i in 1:length(stress_strength)){#loop over different stress period
    stress_data_v = tibble(stress_raw = c(rep(0,stress_start[i]/step), 
                                        rep(stress_strength[i],stress_length[i]/step),#stress period
                                        rep(0,(100 - stress_start[i] - stress_length[i])/step)
                         )) %>% 
      #Cumulative sum of a window using window function rollapply
      mutate(stress_window = rollapply(stress_raw, width = window_width, 
                                       by = 1, FUN = sum, align = "right", fill = 0)) %>% 
      select(!!paste("stress_window",i,sep = "") := stress_window)
    stress_data = bind_cols(stress_data, stress_data_v)
  }

  stress_data = stress_data %>% 
    mutate(stress_window = rowSums(.[2:ncol(stress_data)])) %>% select(age, stress_window)
    
                           
 
  
  #Generate data for 100,000 * 20 = 2,000,000
  time_point = length(seq(from = step, to = 100, by = step)) # number of time points
  stress_data_all = do.call("rbind", replicate(n, stress_data, simplify = FALSE)) %>% 
    bind_cols(id = rep(1:n, each = time_point))#stress data for all hair
  sim_data = as_tibble(data.frame(id = 1:n, rmvnorm(n, rep(0,3), G)))
  names(sim_data) = c("id","b0", "b1", "b2")
  sim_data = left_join(sim_data, stress_data_all)
  dat = tibble(id = rep(1:n, each = time_point), erorr = rnorm(n * time_point,0,sigmae), 
                   age = rep(seq(from = step, to = 100, by = step), n),
                   beta1 = rep(beta1,n * time_point), beta2 = rep(beta2,n * time_point)) %>% 
    left_join(sim_data) %>% 
    mutate(white_no_stress = b0 + (abs(b1) + beta1) * age + erorr, 
           white = b0 + (abs(b1) + beta1) * age + (abs(b2) + beta2) * stress_window + erorr)
  # thres = dat %>% filter(age==70) %>% select(white_no_stress) %>% 
  #   pull() %>% quantile(probs = quantile_percent)
  dat_binay_white = dat %>% 
    mutate(white_binary_no_stress = white_no_stress > thres, white_binary_stress = white > thres)
  dat_summary = dat_binay_white %>% group_by(age) %>% 
    summarise(percentage_white = sum(white_binary_stress)/n)
  return(list(full_dat = dat,percentage_dat = dat_summary,
              thres = thres,stress_dat = stress_data))
}


#Preserve the data after the action button 'runsim'



dat_sim = eventReactive(input$runsim,{
  #get the stress input
  stress_start = as.numeric(str_split(input$stress_start,",")[[1]])
  stress_length = as.numeric(str_split(input$stress_length,",")[[1]])
  stress_strength = as.numeric(str_split(input$stress_strength,",")[[1]])
  #simulate the data
  sim_data_generate(beta1 = input$beta1,beta2 = input$beta2,
                    sigma1 = input$sigma1,sigma2 = input$sigma2,
                    sigma3 = input$sigma3, sigmae = input$sigmae,
                    rho12 = input$rho12,rho13 = input$rho13,
                    rho23 = input$rho23,step = 0.1, 
                    window_width = input$window_width,
                    stress_start = stress_start,
                    stress_strength = stress_strength,
                    stress_length = stress_length,
                    thres = input$thres,# quantile_percent = input$quantile_percent/100
                    n = input$n, seed_num = input$seed_num)
}, ignoreNULL=FALSE)



```
Simulation Plots
=======================================================================



Column {.sidebar}
-----------------------------------------------------------------------

```{r}

actionButton("runsim", "Run simulation",icon("paper-plane"), 
    style="color: #fff; background-color: #337ab7; border-color: #2e6da4")

sliderInput("beta1", 
            tagList("\\( \\beta_{1} \\): Fixed rate of increase in aging factor (slope)"), 
            min = 0, max = 20, 
            value = 16)

sliderInput("beta2", 
            tagList("\\( \\beta_{2} \\): Fixed rate of hair sensitivity to stress (slope)"), 
            min = 0, max = 20, 
            value = 0)

sliderInput("sigma1", 
            tagList("\\( \\sigma_{0} \\): Standard deviation of initial aging factor value at age=0 "), 
            min = 0, max = 50, 
            value = 10)

sliderInput("sigma2", 
            tagList("\\( \\sigma_{1} \\): Standard deviation of aging factor rate across the lifespan "), 
            min = 0, max = 50, 
            value = 13)

sliderInput("sigma3", 
            tagList("\\( \\sigma_{2} \\): Standard deviation of hair sensitivity to stress rate "), 
            min = 0, max = 50, 
            value = 0)

sliderInput("sigmae", 
            tagList("\\( \\sigma_{e} \\): Standard deviation of measurement error"), 
            min = 0, max = 50, 
            value = 10)

sliderInput("rho12", 
            tagList("\\( \\rho_{01} \\): Correlation between initial aging factor value at age 0 (\\( b_i0 \\)) and aging factor rate across the lifespan (\\( b_i1 \\))"), 
            min = 0, max = 1, 
            value = 0)

sliderInput("rho13", 
            tagList("\\( \\rho_{02} \\): Correlation between initial aging factor value at age 0 (\\( b_i0 \\)) and hair sensitivity to stress rate (\\( b_i2 \\))"), 
            min = 0, max = 1, 
            value = 0)

sliderInput("rho23", 
            tagList("\\( \\rho_{12} \\): Correlation between aging factor rate across the lifespan (\\( b_i1 \\)) and hair sensitivity to stress rate (\\( b_i2 \\)) "), 
            min = 0, max = 1, 
            value = 0)

sliderInput("window_width", 
            tagList("Window width: Pre-set number of years accumulating stress before age of evaluation"),
            min = 0, max = 10, 
            value = 0)

textInput("stress_start", 
          tagList("Stress start time: The starting time for stress. For multiple stressors, please separate the different start time with ‘,’"), 
          "0")

textInput("stress_strength", 
          tagList("Stress strenght: please separate the different stress strength with ‘,’"), 
          "0")

textInput("stress_length", 
          tagList("Stress duration: please separate the different stress duration with ‘,’"), 
          "0")
# sliderInput("stress_length1", "Stress length1", min = 0, max = 20, 
#             value = 0.1, step = 0.1)
# sliderInput("stress_strength1", "Stress strength1", min = 0, max = 500, 
#             value = 100)
# 
# sliderInput("stress_length2", "Stress length2", min = 0, max = 10, 
#             value = 10, step = 0.1)
# sliderInput("stress_strength2", "Stress strength2", min = 0, max = 500, 
#             value = 50)




numericInput('thres', tagList("Greying threshold"), 
             value = 1920)
# sliderInput("quantile_percent", 
#             tagList("Threshold", helpPopup("The whiteloading threshold for hair turning white")), 
#             min = 1, max = 99, 
#             value = 75)
sliderInput("n", 
            tagList("N: Number of hairs in simulation"), 
            min = 100, max = 1000, 
            value = 1000)
numericInput("seed_num", 
             tagList("Simulation seed number"),
             value = 2019)
sliderInput("per_hair_show",
            tagList("Percent of hairs to show in the white loading plot"),
            min = 1, max = 100, value=10)



```


Column {data-width=650}
-----------------------------------------------------------------------

### Hair greying trajectories

```{r 1}
renderPlotly({
  
  ggplotly(
    dat_sim()$percentage_dat %>% 
      ggplot(aes(x = age, y = percentage_white)) + geom_col(color = "light blue") +
      theme_bw()  + scale_y_continuous(labels = scales::percent, limits = c(0,1)) + 
      labs(y = "Proportion of grey hairs", x = "Age (years)")
  )

})
```

Column {data-width=350}
-----------------------------------------------------------------------

### Accumulation of aging factor in individual hairs

```{r 2}
renderPlot({
  
  
  dat_sim()$full_dat %>% 
    #sample certain amount of hair to display
    filter(id %in% sample(1:input$n, round(input$n*(input$per_hair_show/100),0)) ) %>% 
    ggplot(aes(x = age, y = white, group = id)) + 
    geom_line(color = "light blue", alpha = 0.5)  +
    geom_hline(yintercept=dat_sim()[[3]], 
               color = "red") + theme_bw() + labs(y = "Aging Factor (A.U.)", x = "Age (years)")


})
```

### Window of stress

```{r 3}
renderPlot({
  
  dat_sim()$stress_dat %>% 
    ggplot(aes(x = age, y = stress_window)) + geom_line(color = "light blue") +
    theme_bw() + labs(y = "Stress Window (A.U.)", x = "Age (years)")

})


```

Download data
=======================================================================



Column {data-width=200}
-----------------------------------------------------------------------
### Percentage of white hair 
```{r}
downloadButton("downloadPer", "Download")
downloadHandler(
    filename = function() {
      paste("percentage_data.csv")
    },
    content = function(file) {
      write.csv(dat_sim()$percentage_dat, file, row.names = F)
    }
  )
renderDataTable({
  datatable(dat_sim()$percentage_dat,rownames = F)
})

```


Column {data-width=200}
-----------------------------------------------------------------------
### Stress data
```{r}
downloadButton("downloadStress", "Download")
downloadHandler(
    filename = function() {
      paste("stress_data.csv")
    },
    content = function(file) {
      write.csv(dat_sim()$stress_dat, file, row.names = F)
    }
  )
renderDataTable({
  datatable(dat_sim()$stress_dat,rownames = F)
})
```

Column {data-width=400}
----------------------------------------------------------------------
### Full simulation data
```{r}
downloadButton("downloadFull", "Download")
downloadHandler(
    filename = function() {
      paste("full_data.csv")
    },
    content = function(file) {
      write.csv(dat_sim()$full_dat, file, row.names = F)
    }
  )
renderDataTable({
  datatable(
    (dat_sim()$full_dat %>% 
    select(-white_no_stress) %>% 
    rename(stress = stress_window)),
    rownames = F
  ) 
})

```




Details
=======================================================================
To simulate the greying process for a fixed person, we built a linear mixed model for the $i$th ($i=1,\ldots,\ n$) hair with two fixed effects ($\beta_1$ aging factor rate and $\beta_2$ hair sensitivity to stress rate) and three random effects ( $b_{i0}$ for initial aging factor value at age 0, $b_{i1}$ for aging factor rate across the lifespan and $b_{i2}$ for hair sensitivity to stress rate). We are taking the absolute value of these random effects because the slopes should be positive for age and accumulating stress. 
$$AgingFactorValue_{\left\{i,\ age\right\}}=\left|b_{i0}\right|+\left(\left|b_{i1}\right|+\beta_1\right)age+\left(\left|b_{i2}\right|+\beta_2\right)AccumulatingStress_{\left\{age\right\}}+e_i$$

Where AccumulatingStress is defined as:
$$
AccumulatingStress_{age}=\sum_{a=age-WindowWidth}^{age}{stress_a}
$$

The three random effects follow a multivariate normal:
$$\left(b_{i0},b_{i1},b_{i2}\right)\sim N\left(0,G\right)$$
with mean zero and a covariance structure:
$$
G=\left[\begin{matrix}\sigma_0^2&\rho_{01}\sigma_0\sigma_1&\rho_{02}\sigma_0\sigma_2\\\rho_{01}\sigma_1\sigma_0&\sigma_1^2&\rho_{12}\sigma_1\sigma_2\\\rho_{02}\sigma_2\sigma_0&\rho_{12}\sigma_2\sigma_1&\sigma_2^2\\\end{matrix}\right]
$$
All the correlation $\rho_{01},\rho_{02},\rho_{12}$ in the simulation are set to be positive.  
When the AgingFactorValue for ith hair reach a predefined threshold, the ith hair will turn grey. 
  
  
**Description for the parameters that could be changed (i is the fix person we are simulating)**  
1. $\beta_1$:  Fixed rate of increase in aging factor (slope)  
2. $\beta_2$:  Fixed rate of hair sensitivity to stress (slope)  
3. $\sigma_0$:  Standard deviation of initial aging factor value at age 0 for $b_{i0}$  
4. $\sigma_1$:  Standard deviation of aging factor rate across the lifespan for $b_{i1}$  
5. $\sigma_2$:  Standard deviation of hair sensitivity to stress rate for $b_{i2}$  
6. $\sigma_e$:  Standard deviation of measurement error for $e_i$  
7. $\rho_{01}$: Correlation between initial aging factor value at age 0 ($b_{i0}$) and aging factor rate across the lifespan ($b_{i1}$)  
8. $\rho_{02}$: Correlation between initial aging factor value at age 0 ($b_{i0}$) and hair sensitivity to stress rate ($b_{i2}$)  
9. $\rho_{12}$: Correlation between aging factor rate across the lifespan ($b_{i1}$) and hair sensitivity to stress rate ($b_{i2}$)  
10. Window width:  Pre-set number of years accumulating stress before age of evaluation.  
11. Stress start time: The starting time for stress. For multiple stressors, please separate the different start time with ','    
12. Stress strength: please separate the different stress strength with ','  
13. Stress duration: please separate the different stress duration with ','  
14. Threshold: The whiteloading threshold for hair turning white  
15. N: Number of hairs in simulation  
16. Seed number: Simulation seed number


About
=======================================================================
This app was created by *Junting Ren and Todd Ogden*.  
For more information please refer to the manuscript: Quantitative Mapping of Human Hair Greying and Reversal in Relation to Life Stress  
Ayelet Rosenberg, Shannon Rausser, Junting Ren, Eugene Mosharov, R Todd Ogden, Clay Lacefield, Gabriel Sturm, Ralf Paus, Martin Picard

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Contact— JavaScript license information— Web API

back to top