Chapter 2 Exploration Diagnostic

2.1 Setup

First, we need to do some set up to analyze our data

Include dependencies

library(ggplot2)       # For plotting
library(tidyverse)     # For data wrangling
library(knitr)         # For making nice rmarkdown documents
library(cowplot)       # For theme
library(viridis)       # For color scale
library(RColorBrewer)  # For more color scales
library(rstatix)
library(ggsignif)      # For adding pairwise significance to plots
library(Hmisc)         # For bootstrapping condifence internvals
library(kableExtra)    # For displaying nice tables
source("https://gist.githubusercontent.com/benmarwick/2a1bb0133ff568cbe28d/raw/fb53bd97121f7f9ce947837ef1a4c65a73bffb3f/geom_flat_violin.R") # For raincloud plots
library(readr)        # For reading in data
library(stringr)      # For manipulating string data
library(ggpubr)       # For displaying correlation statistics on plots
library(infotheo)     # For causality analysis
library(scales)       # For displaying scales nicely in facetted plots
library(osfr)         # For downloading the data for this project

These analyses were conducted in the following computing environment:

print(version)
##                _                           
## platform       x86_64-apple-darwin17.0     
## arch           x86_64                      
## os             darwin17.0                  
## system         x86_64, darwin17.0          
## status                                     
## major          4                           
## minor          1.1                         
## year           2021                        
## month          08                          
## day            10                          
## svn rev        80725                       
## language       R                           
## version.string R version 4.1.1 (2021-08-10)
## nickname       Kick Things

Setup constants to be used across plots

# Labeler for stats annotations
p_label <- function(p_value) {
  threshold = 0.0001
  if (p_value < threshold) {
    return(paste0("p < ", threshold))
  } else {
    return(paste0("p = ", p_value))
  }
}

# Significance threshold
alpha <- 0.05

# Common graph variables
performance_ylim <- 1
coverage_ylim <- 1.0

####### misc #######
# Configure our default graphing theme
theme_set(theme_cowplot())

The data for this project are hosted on osf.io. The following chunk downloads them automatically if they haven’t already been downloaded.

# Read in data
osf_retrieve_file("esm4r") %>% osf_download(conflicts = "skip")  # Download data from osf
data_loc <- "final_exploration_diagnostic_data.csv"

data <- read_csv(data_loc, na=c("NONE", "NA", ""))

## Clean up data columns

# Make selection name column human readable
data <- data %>% mutate(selection_name = as.factor(case_when(
  selection_name == "EpsilonLexicase" ~ "Lexicase",
  TOUR_SIZE == 1 ~ "Random",
  selection_name == "Tournament" ~ "Tournament",
  selection_name == "FitnessSharing" ~ "Fitness Sharing",
  selection_name == "EcoEa" ~ "EcoEA"
)))

# Calculate performance statistics.
# Elite trait avg is the avg per-site performance of the best individual
data$elite_trait_avg <-
  data$ele_agg_per / data$OBJECTIVE_CNT
data$unique_start_positions_coverage <-
  data$uni_str_pos / data$OBJECTIVE_CNT

# Convert elite_trait_avg to percent of maximum possible
data$elite_trait_avg <- data$elite_trait_avg/data$TARGET

# Grab data from just the final time point
final_data <- filter(data, evaluations==max(data$evaluations))

2.2 Performance

For context, it’s important to know how each selection scheme performed on the exploration diagnostic.

2.2.1 Over time

First we look at the dynamics of performance over time.

2.2.1.1 Trait performance

Here, we plot average trait performance (i.e. fitness) over time for each selection scheme. We log the x-axis because Eco-EA gains fitness over a very long time scale, whereas the interesting dynamics for the other selection schemes occur relatively quickly.

ggplot(
    data,
    aes(
      x=gen,                 # Generations
      y=elite_trait_avg,     # Performance
      color=selection_name,  # Selection scheme
      fill=selection_name
    )
  ) +
  stat_summary(geom="line", fun=mean) + # Plot line showing mean for each selection scheme
  stat_summary(  # Add shading around each line indicating 95% confiedence interval
    geom="ribbon",
    fun.data="mean_cl_boot",
    fun.args=list(conf.int=0.95),
    alpha=0.2,
    linetype=0
  ) +
  scale_y_continuous(
    name="Average trait performance", # Set y axis title
    limits=c(0, performance_ylim)  # Set y axis range to include all possible performance values
  ) +
  scale_x_log10(  # Log x axis
    name="Generation" # Set x axis title
  ) +
  scale_color_discrete("Selection") + # Set legend title
  scale_fill_discrete("Selection")    # Set legend title

As observed by Hernandez et al. in their original paper on the exploration diagnostic (Hernandez, Lalejini, and Ofria 2021), fitness in tournament selection initially increases quickly and then plateaus. Fitness in lexicase selection increases slightly slower but plateaus at a much higher value (nearly 100%). Fitness sharing behaves similarly to tournament selection, but maintains a slight upward trajectory (note that, because the x axis is on a log scale, this trajectory is very gradual). Eco-EA takes substantially longer to increase in fitness but ultimately surpasses fitness sharing and tournament selection. It is unclear whether it would pass lexicase selection if these runs were allowed to continue for slightly longer; they do not appear to have plateaued yet. We chose to cut them off at 500,000 generations due to resource constraints and the fact that the questions we’re asking here are not really about final fitness.

2.2.1.2 Activation position coverage

Out of curiosity, we also ran the analysis of unique activation positions present in the population, used by Hernandez et. al. This analysis tells us about the diversity of start positions for the coding region represented in the population. As the set of start positions in the population tends to represent a meaningful constraint on the number of paths through the fitness landscape that are currently accessible, this is in some sense a metric of useful diversity in the population

ggplot(data, aes(x=gen, y=unique_start_positions_coverage, color=selection_name, fill=selection_name)) +
  stat_summary(geom="line", fun=mean) +
  stat_summary(
    geom="ribbon",
    fun.data="mean_cl_boot",
    fun.args=list(conf.int=0.95),
    alpha=0.2,
    linetype=0
  ) +
  scale_y_continuous(
    name="Activation position coverage"
  ) +
  scale_x_log10(
    name="Generation"
  ) +
  scale_color_discrete("Selection")+
  scale_fill_discrete("Selection")

We see that lexicase selection maintains by far that largest number of unique start positions, even surpassing the number maintained by random drift. This suggests that lexicase selection is actively selecting for maintaining a diversity of start positions. Tournament selection and fitness sharing perform virtually identically, with Eco-EA falling in between.

2.2.2 Final

While trends over time are more informative, it can be hard to visualize the full distribution (particularly the extent of variation). Thus, we also conduct a more detailed analysis of performance at the final time point.

2.2.2.1 Trait performance

First we conduct statistics to identify which selection schemes are significantly different from each other.

# Compute manual labels for geom_signif
stat.test <- final_data %>%
  wilcox_test(elite_trait_avg ~ selection_name) %>%
  adjust_pvalue(method = "bonferroni") %>%  # Apply Bonferroni correction for multiple comparisons
  add_significance() %>%
  add_xy_position(x="selection_name",step.increase=1)
stat.test$label <- mapply(p_label,stat.test$p.adj)

Then we make raincloud plots (Allen et al. 2021) of each selection scheme.

elite_final_performance_fig <- ggplot(
    final_data,
    aes(
      x=selection_name,
      y=elite_trait_avg,
      fill=selection_name
    )
  ) +
  geom_flat_violin(
    position = position_nudge(x = .2, y = 0),
    alpha = .8,
    scale="width"
  ) +
  geom_point(
    mapping=aes(color=selection_name),
    position = position_jitter(width = .15),
    size = .5,
    alpha = 0.8
  ) +
  geom_boxplot(
    width = .1,
    outlier.shape = NA,
    alpha = 0.5
  ) +
  scale_y_continuous(
    name="Average trait performance",
    limits=c(0, performance_ylim)
  ) +
  scale_x_discrete(
    name="Selection"
  ) +
  scale_fill_discrete(
    name="Selection"
  ) +
  scale_color_discrete(
    name="Selection"
  ) + 
  theme(legend.position="none")
elite_final_performance_fig

These observations look fairly consistent with the timeseries plots.

Next, we output the results of our significance testing.

stat.test %>%
  kbl() %>%
  kable_styling(
    bootstrap_options = c(
      "striped",
      "hover",
      "condensed",
      "responsive"
    )
  ) %>%
  scroll_box(width="600px")
.y. group1 group2 n1 n2 statistic p p.adj p.adj.signif y.position groups xmin xmax label
elite_trait_avg EcoEA Fitness Sharing 50 50 1561 3.20e-02 3.20e-01 ns 1.922000 EcoEA , Fitness Sharing 1 2 p = 0.32
elite_trait_avg EcoEA Lexicase 50 47 60 0.00e+00 0.00e+00 **** 2.946444 EcoEA , Lexicase 1 3 p < 1e-04
elite_trait_avg EcoEA Random 50 50 2500 0.00e+00 0.00e+00 **** 3.970889 EcoEA , Random 1 4 p < 1e-04
elite_trait_avg EcoEA Tournament 50 50 1939 2.10e-06 2.07e-05 **** 4.995333 EcoEA , Tournament 1 5 p < 1e-04
elite_trait_avg Fitness Sharing Lexicase 50 47 593 2.69e-05 2.69e-04 *** 6.019778 Fitness Sharing, Lexicase 2 3 p = 0.000269
elite_trait_avg Fitness Sharing Random 50 50 2500 0.00e+00 0.00e+00 **** 7.044222 Fitness Sharing, Random 2 4 p < 1e-04
elite_trait_avg Fitness Sharing Tournament 50 50 1549 4.00e-02 4.00e-01 ns 8.068667 Fitness Sharing, Tournament 2 5 p = 0.4
elite_trait_avg Lexicase Random 47 50 2350 0.00e+00 0.00e+00 **** 9.093111 Lexicase, Random 3 4 p < 1e-04
elite_trait_avg Lexicase Tournament 47 50 2098 0.00e+00 0.00e+00 **** 10.117556 Lexicase , Tournament 3 5 p < 1e-04
elite_trait_avg Random Tournament 50 50 10 0.00e+00 0.00e+00 **** 11.142000 Random , Tournament 4 5 p < 1e-04

Fitness sharing did not perform significantly differently from Eco-EA or Tournament selection, but all other selection schemes are significantly different.

2.2.2.2 Final activation position Coverage

Now we do the same analysis for final activation position coverage.

First we calculate the statistics

# Compute manual labels for geom_signif
stat.test <- final_data %>%
  wilcox_test(unique_start_positions_coverage ~ selection_name) %>%
  adjust_pvalue(method = "bonferroni") %>%
  add_significance() %>%
  add_xy_position(x="selection_name",step.increase=1)
stat.test$manual_position <- stat.test$y.position * 1.05
stat.test$label <- mapply(p_label,stat.test$p.adj)

Then we make raincloud plots

unique_start_positions_coverage_final_fig <- ggplot(
    final_data,
    aes(
      x=selection_name,
      y=unique_start_positions_coverage,
      fill=selection_name
    )
  ) +
  geom_flat_violin(
    position = position_nudge(x = .2, y = 0),
    alpha = .8,
    scale="width"
  ) +
  geom_point(
    mapping=aes(color=selection_name),
    position = position_jitter(width = .15),
    size = .5,
    alpha = 0.8
  ) +
  geom_boxplot(
    width = .1,
    outlier.shape = NA,
    alpha = 0.5
  ) +
  scale_y_continuous(
    name="Activation position coverage",
    limits=c(0, coverage_ylim)
  ) +
  scale_x_discrete(
    name="Selection"
  ) +
  scale_fill_discrete(
    name="Selection"
  ) +
  scale_color_discrete(
    name="Selection"
  ) +
  theme(
    legend.position="none"
  )
unique_start_positions_coverage_final_fig

These also look unsurprising.

Lastly, we output the results of significance testing.

stat.test %>%
  kbl() %>%
  kable_styling(
    bootstrap_options = c(
      "striped",
      "hover",
      "condensed",
      "responsive"
    )
  ) %>%
  scroll_box(width="600px")
.y. group1 group2 n1 n2 statistic p p.adj p.adj.signif y.position groups xmin xmax manual_position label
unique_start_positions_coverage EcoEA Fitness Sharing 50 50 2392.5 0.000 0 **** 1.420000 EcoEA , Fitness Sharing 1 2 1.491000 p < 1e-04
unique_start_positions_coverage EcoEA Lexicase 50 47 0.0 0.000 0 **** 2.175556 EcoEA , Lexicase 1 3 2.284333 p < 1e-04
unique_start_positions_coverage EcoEA Random 50 50 339.0 0.000 0 **** 2.931111 EcoEA , Random 1 4 3.077667 p < 1e-04
unique_start_positions_coverage EcoEA Tournament 50 50 2387.0 0.000 0 **** 3.686667 EcoEA , Tournament 1 5 3.871000 p < 1e-04
unique_start_positions_coverage Fitness Sharing Lexicase 50 47 0.0 0.000 0 **** 4.442222 Fitness Sharing, Lexicase 2 3 4.664333 p < 1e-04
unique_start_positions_coverage Fitness Sharing Random 50 50 25.0 0.000 0 **** 5.197778 Fitness Sharing, Random 2 4 5.457667 p < 1e-04
unique_start_positions_coverage Fitness Sharing Tournament 50 50 1274.5 0.708 1 ns 5.953333 Fitness Sharing, Tournament 2 5 6.251000 p = 1
unique_start_positions_coverage Lexicase Random 47 50 2350.0 0.000 0 **** 6.708889 Lexicase, Random 3 4 7.044333 p < 1e-04
unique_start_positions_coverage Lexicase Tournament 47 50 2350.0 0.000 0 **** 7.464444 Lexicase , Tournament 3 5 7.837667 p < 1e-04
unique_start_positions_coverage Random Tournament 50 50 2475.5 0.000 0 **** 8.220000 Random , Tournament 4 5 8.631000 p < 1e-04

2.3 Phylogenetic diversity

Next, we analyze the behavior of phylogenetic diversity on the exploration diagnostic.

2.3.1 Relationship between different types of pylogenetic diversity

First, to get a big-picture overview, we make correlation matrices of all the different phylogenetic diversity metrics:

final_data %>% 
  transmute(MinPD=min_phenotype_pairwise_distance, 
            MeanPD=mean_phenotype_pairwise_distance, 
            MaxPD=max_phenotype_pairwise_distance, 
            VarPD=variance_phenotype_pairwise_distance, 
            MinED = min_phenotype_evolutionary_distinctiveness,
            MeanED= mean_phenotype_evolutionary_distinctiveness,
            MaxED=max_phenotype_evolutionary_distinctiveness,
            VarED=variance_phenotype_evolutionary_distinctiveness,
            PD=phenotype_current_phylogenetic_diversity,  # See Faith 1992
            MRCA=phen_mrca_depth,  # Phylogenetic depth of most recent common ancestor
            N=phen_num_taxa     # Number of taxonomically-distinct phenotypes
  ) %>%
  cor_mat() %>% 
  pull_lower_triangle() %>% 
  cor_plot()

However, these correlations may well vary by selection scheme, and even over time within a selection scheme. Let’s take a look at some scatter plots.

ggplot(
    data %>% filter(gen==500000),
    aes(
        y=mean_phenotype_pairwise_distance,
        x=variance_phenotype_pairwise_distance,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
  scale_x_continuous(
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

Mean and variance of pairwise distances appears to correlate fairly closely with each other across selection schemes. The exception to this is tournament selection, where the range of values for both of these metrics are very small and the correlation is directly inverse. This is likely being driven by there being a small number of taxa that are mostly siblings of each other.

ggplot(
    data %>% filter(gen==500000),
    aes(
        y=mean_phenotype_pairwise_distance,
        x=max_phenotype_pairwise_distance,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
  scale_x_continuous(
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

Maximum and mean pairwise distance also correlate pretty closely, with the exception again being tournament. These data shed further light on the previous graph as well - the maximum pariwise distance for tournament selection is always 2.

ggplot(
    data %>% filter(gen==500000),
    aes(
        y=variance_phenotype_pairwise_distance,
        x=max_phenotype_pairwise_distance,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
  scale_x_continuous(
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

Max and variance of pairwise distances behave similarly.

And let’s check that it doesn’t look radically different early on in the run:

ggplot(
    data %>% filter(gen==5000),
    aes(
        y=mean_phenotype_pairwise_distance,
        x=variance_phenotype_pairwise_distance,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
  scale_x_continuous(
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

The correlation between these two metrics is substantially weaker for Eco-Ea and lexicase selection at the early time point.

ggplot(
    data %>% filter(gen==5000),
    aes(
        y=mean_phenotype_pairwise_distance,
        x=max_phenotype_pairwise_distance,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
  scale_x_continuous(
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

This relationship is also weaker, although not as much weaker.

ggplot(
    data %>% filter(gen==5000),
    aes(
        y=variance_phenotype_pairwise_distance,
        x=max_phenotype_pairwise_distance,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
  scale_x_continuous(
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

Similarly, the correlation between max and variance of pariwise distances is weakend, but still robust. So the relationship between mean and variance of pairwise distances is the only one that is really weaker at the earlier time points. Understanding exactly what drives these discrepancies is a promising angle for future work, as it may shed further light on the dynamics occurring in the run. It is particularly interesting that EcoEA and Lexicase selection are the scenarios where they diverge.

Similarly, the evolutionary distinctiveness metrics look largely similar to each other, but let’s spot check that too.

ggplot(
    data %>% filter(gen==500000),
    aes(
        y=mean_phenotype_evolutionary_distinctiveness,
        x=variance_phenotype_evolutionary_distinctiveness,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
  scale_x_continuous(
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

These aren’t the strongest correlations; different information is definitely being captured by each metric. However, they still correlate to a fair extent.

ggplot(
    data %>% filter(gen==500000),
    aes(
        y=mean_phenotype_evolutionary_distinctiveness,
        x=max_phenotype_evolutionary_distinctiveness,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
  scale_x_continuous(
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

Again, for the most part there is a fairly high degree of correlation here.

ggplot(
    data %>% filter(gen==500000),
    aes(
        y=mean_phenotype_evolutionary_distinctiveness,
        x=min_phenotype_evolutionary_distinctiveness,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
  scale_x_continuous(
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

And here too, although it’s getting kind of weak for lexicase.

And let’s check out an earlier time point

ggplot(
    data %>% filter(gen==5000),
    aes(
        y=mean_phenotype_evolutionary_distinctiveness,
        x=variance_phenotype_evolutionary_distinctiveness,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
  scale_x_continuous(
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

The relationship between min and mean evolutionary distinctiveness holds up at the earlier time point for everything except fitness sharing.

ggplot(
    data %>% filter(gen==5000),
    aes(
        y=mean_phenotype_evolutionary_distinctiveness,
        x=max_phenotype_evolutionary_distinctiveness,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
  scale_x_continuous(
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

And here, for everything except lexicase

ggplot(
    data %>% filter(gen==5000),
    aes(
        y=mean_phenotype_evolutionary_distinctiveness,
        x=min_phenotype_evolutionary_distinctiveness,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
  scale_x_continuous(
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

These are relatively weak for the most part.

Looks like the evolutionary distinctiveness statistics capture more different information from each other than the pairwise distance statistics (in general the correlations are weaker, and there are more deviations from the pattern of positive correlations). However, in a lot of cases they still correlate. There is definitely more exploration to be done on what differences in these metrics imply, but for simplicity we’ll stick to one pairwise distance metric and one evolutionary distinctiveness metric.

Since it’s hard to get a gestalt impression from the scatterplots, let’s take a look at the correlation matrices for each selection scheme. Note that these are at the final time point, which clearly misses some dynamics that occurr earlier.

2.3.1.1 Random

final_data %>% 
  filter(selection_name=="Random") %>%
  transmute(MinPD=min_phenotype_pairwise_distance, 
            MeanPD=mean_phenotype_pairwise_distance, 
            MaxPD=max_phenotype_pairwise_distance, 
            VarPD=variance_phenotype_pairwise_distance, 
            MinED = min_phenotype_evolutionary_distinctiveness,
            MeanED= mean_phenotype_evolutionary_distinctiveness,
            MaxED=max_phenotype_evolutionary_distinctiveness,
            VarED=variance_phenotype_evolutionary_distinctiveness,
            PD=phenotype_current_phylogenetic_diversity,  # See Faith 1992
            MRCA=phen_mrca_depth,  # Phylogenetic depth of most recent common ancestor
            N=phen_num_taxa     # Number of taxonomically-distinct phenotypes
  ) %>%
  cor_mat() %>% 
  pull_lower_triangle() %>% 
  cor_plot()

In random selection, all the pairwise distance metrics correlate with each other, and also more weakly with some of the evolutionary distinctiveness metrics, but not max and variance (although they correlate strongly with each other). It is somewhat surprising that the depth of the most recent common ancestor and Faith’s Phylogenetic Diveristy metric have so little correlation with the others.

2.3.1.2 Tournament

final_data %>% 
  filter(selection_name=="Tournament") %>%
  transmute(MinPD=min_phenotype_pairwise_distance, 
            MeanPD=mean_phenotype_pairwise_distance, 
            MaxPD=max_phenotype_pairwise_distance, 
            VarPD=variance_phenotype_pairwise_distance, 
            MinED = min_phenotype_evolutionary_distinctiveness,
            MeanED= mean_phenotype_evolutionary_distinctiveness,
            MaxED=max_phenotype_evolutionary_distinctiveness,
            VarED=variance_phenotype_evolutionary_distinctiveness,
            PD=phenotype_current_phylogenetic_diversity,  # See Faith 1992
            MRCA=phen_mrca_depth,  # Phylogenetic depth of most recent common ancestor
            N=phen_num_taxa     # Number of taxonomically-distinct phenotypes
  ) %>%
  cor_mat() %>% 
  pull_lower_triangle() %>% 
  cor_plot()

Here, all the evolutionary distinctiveness metrics coreelate strongly with each other, but the pairiwse distance metrics less so (although this is likely an artifact of the extremely low phylogenetic diversity in tournament selection)

2.3.1.3 Fitness Sharing

final_data %>% 
  filter(selection_name=="Fitness Sharing") %>%
  transmute(MinPD=min_phenotype_pairwise_distance, 
            MeanPD=mean_phenotype_pairwise_distance, 
            MaxPD=max_phenotype_pairwise_distance, 
            VarPD=variance_phenotype_pairwise_distance, 
            MinED = min_phenotype_evolutionary_distinctiveness,
            MeanED= mean_phenotype_evolutionary_distinctiveness,
            MaxED=max_phenotype_evolutionary_distinctiveness,
            VarED=variance_phenotype_evolutionary_distinctiveness,
            PD=phenotype_current_phylogenetic_diversity,  # See Faith 1992
            MRCA=phen_mrca_depth,  # Phylogenetic depth of most recent common ancestor
            N=phen_num_taxa     # Number of taxonomically-distinct phenotypes
  ) %>%
  cor_mat() %>% 
  pull_lower_triangle() %>% 
  cor_plot()

Here, the pairwise distance metrics correlate with each other and the evolutionary distinctivenes metrics correlated with each other. There are some correlations between pairwise distance and evolutionary distinctiveness metrics, but they’re weaker.

2.3.1.4 Lexicase selection

final_data %>% 
  filter(selection_name=="Lexicase") %>%
  transmute(MinPD=min_phenotype_pairwise_distance, 
            MeanPD=mean_phenotype_pairwise_distance, 
            MaxPD=max_phenotype_pairwise_distance, 
            VarPD=variance_phenotype_pairwise_distance, 
            MinED = min_phenotype_evolutionary_distinctiveness,
            MeanED= mean_phenotype_evolutionary_distinctiveness,
            MaxED=max_phenotype_evolutionary_distinctiveness,
            VarED=variance_phenotype_evolutionary_distinctiveness,
            PD=phenotype_current_phylogenetic_diversity,  # See Faith 1992
            MRCA=phen_mrca_depth,  # Phylogenetic depth of most recent common ancestor
            N=phen_num_taxa     # Number of taxonomically-distinct phenotypes
  ) %>%
  cor_mat() %>% 
  pull_lower_triangle() %>% 
  cor_plot()

Again, the pairwise distance metrics correlate with each other but not wth the volutionary distinctivness metrics. The evolutionary distinctiveness metrics sort of correlate with each other.

2.3.1.5 Eco-EA

final_data %>% 
  filter(selection_name=="EcoEA") %>%
  transmute(MinPD=min_phenotype_pairwise_distance, 
            MeanPD=mean_phenotype_pairwise_distance, 
            MaxPD=max_phenotype_pairwise_distance, 
            VarPD=variance_phenotype_pairwise_distance, 
            MinED = min_phenotype_evolutionary_distinctiveness,
            MeanED= mean_phenotype_evolutionary_distinctiveness,
            MaxED=max_phenotype_evolutionary_distinctiveness,
            VarED=variance_phenotype_evolutionary_distinctiveness,
            PD=phenotype_current_phylogenetic_diversity,  # See Faith 1992
            MRCA=phen_mrca_depth,  # Phylogenetic depth of most recent common ancestor
            N=phen_num_taxa     # Number of taxonomically-distinct phenotypes
  ) %>%
  cor_mat() %>% 
  pull_lower_triangle() %>% 
  cor_plot()

In Eco-EA, there is an impressively strong positive correlation among all the phylodiversity metrics. Understanding what causes Eco-EA to deviate from lexicase selection (and to a lesser extent fitness sharing) in this way would be worthy of further research.

2.3.2 Over time

2.3.2.1 Mean pairwise distance

First we plot mean pairwise distance over time. We log the y axis because there is such variation in mean pairwise distance across selection schemes, and the x-axis for the same reason as before.

ggplot(
    data,
    aes(
      x=gen,
      y=mean_phenotype_pairwise_distance,
      color=selection_name,
      fill=selection_name
    )
  ) +
  stat_summary(geom="line", fun=mean) +
  stat_summary(
    geom="ribbon",
    fun.data="mean_cl_boot",
    fun.args=list(conf.int=0.95),
    alpha=0.2,
    linetype=0
  ) +
  scale_y_log10(
    name="Mean pairwise distance"
  ) +
  scale_x_log10(
    name="Generation"
  ) +
  scale_color_discrete("Selection") +
  scale_fill_discrete("Selection")

Lexicase selection maintains a monotonic increase in phylogenetic diversity over the course of the entire experiment. It likely never experiences a full coalescence event (where the most-recent common ancestor changes). Eco-EA nearly keeps pace with lexicase selection until towards the end, when its phylogenetic diversity crashes. This is likely the result of selective sweeps that begin to occur as the population discovers high fitness solutions. Fitness sharing shows a slight dip at the same time that its fitness plateaus (likely also the result of a selective sweep), but phylogenetic diversity recovers afterwards, making for a relatively constant level. over time. Tournament selection, on the other hand, maintains the same (low) level of phylogenetic diversity as fitness sharing, up until the point that fitness plateaus, at which point tournament selection’s phylodiversity drops to nearly 0. Interestingly, lexicase selection and Eco-EA both maintain more phylodiversity than random selection, whereas fitness sharing and tournament selection maintain less.

2.3.2.2 Mean evolutionary distinctiveness

For comparison, we make the same plot with mean evolutionary distinctiveness.

ggplot(
    data,
    aes(
      x=gen,
      y=mean_phenotype_evolutionary_distinctiveness,
      color=selection_name,
      fill=selection_name
    )
  ) +
  stat_summary(geom="line", fun=mean) +
  stat_summary(
    geom="ribbon",
    fun.data="mean_cl_boot",
    fun.args=list(conf.int=0.95),
    alpha=0.2,
    linetype=0
  ) +
  scale_y_log10(
    name="Mean evolutionary distinctiveness"
  ) +
  scale_x_log10(
    name="Generation"
  ) +
  scale_color_discrete("Selection") +
  scale_fill_discrete("Selection")

Interestingly, Fitness Sharing and tournament selection both start increasing in evolutionary distinctiveness only after their fitnesses have plateaued. This seems likely to be due to some sort of pathological behavior of mean evolutionary distinctiveness on small trees, but more investigation would be necessary to figure out exactly what’s going on. Trends in other selection schemes are largely similar.

2.3.3 Final

Next, we perform a more in-depth analysis of phylodiversity distributions at the final time point.

2.3.3.1 Mean pairwise distance

First, we test which selection schemes end up with significantly different final distributions of mean pairwise distance.

# Pairwise wilcoxon teset to determine which conditions are significantly different from each other
stat.test <- final_data %>%
  wilcox_test(mean_phenotype_pairwise_distance ~ selection_name) %>%
  adjust_pvalue(method = "bonferroni") %>%
  add_significance() %>%
  add_xy_position(x="selection_name",step.increase=1)
stat.test$label <- mapply(p_label,stat.test$p.adj)

# Output stats
stat.test %>%
  kbl() %>%
  kable_styling(
    bootstrap_options = c(
      "striped",
      "hover",
      "condensed",
      "responsive"
    )
  ) %>%
  scroll_box(width="600px")
.y. group1 group2 n1 n2 statistic p p.adj p.adj.signif y.position groups xmin xmax label
mean_phenotype_pairwise_distance EcoEA Fitness Sharing 50 50 1824.0 7.70e-05 7.70e-04 *** 49488.81 EcoEA , Fitness Sharing 1 2 p = 0.00077
mean_phenotype_pairwise_distance EcoEA Lexicase 50 47 227.0 0.00e+00 0.00e+00 **** 76981.49 EcoEA , Lexicase 1 3 p < 1e-04
mean_phenotype_pairwise_distance EcoEA Random 50 50 690.0 1.15e-04 1.15e-03 ** 104474.18 EcoEA , Random 1 4 p = 0.00115
mean_phenotype_pairwise_distance EcoEA Tournament 50 50 2500.0 0.00e+00 0.00e+00 **** 131966.86 EcoEA , Tournament 1 5 p < 1e-04
mean_phenotype_pairwise_distance Fitness Sharing Lexicase 50 47 0.0 0.00e+00 0.00e+00 **** 159459.54 Fitness Sharing, Lexicase 2 3 p < 1e-04
mean_phenotype_pairwise_distance Fitness Sharing Random 50 50 536.0 9.00e-07 8.70e-06 **** 186952.22 Fitness Sharing, Random 2 4 p < 1e-04
mean_phenotype_pairwise_distance Fitness Sharing Tournament 50 50 2232.5 0.00e+00 0.00e+00 **** 214444.90 Fitness Sharing, Tournament 2 5 p < 1e-04
mean_phenotype_pairwise_distance Lexicase Random 47 50 2350.0 0.00e+00 0.00e+00 **** 241937.58 Lexicase, Random 3 4 p < 1e-04
mean_phenotype_pairwise_distance Lexicase Tournament 47 50 2350.0 0.00e+00 0.00e+00 **** 269430.26 Lexicase , Tournament 3 5 p < 1e-04
mean_phenotype_pairwise_distance Random Tournament 50 50 2500.0 0.00e+00 0.00e+00 **** 296922.94 Random , Tournament 4 5 p < 1e-04

Looks like they are all significantly different from each other.

# Raincloud plot of final mean pairwise distance
final_phylogeny_fig <- ggplot(
    final_data,
    aes(
      x=selection_name,
      y=mean_phenotype_pairwise_distance,
      fill=selection_name
    )
  ) +
  geom_flat_violin(
    position = position_nudge(x = .2, y = 0),
    alpha = .8,
    scale="width"
  ) +
  geom_point(
    mapping=aes(color=selection_name),
    position = position_jitter(width = .15),
    size = .5,
    alpha = 0.8
  ) +
  geom_boxplot(
    width = .1,
    outlier.shape = NA,
    alpha = 0.5
  ) +
  scale_y_log10(
    name="Mean pairwise distance"
  ) +
  scale_x_discrete(
    name="Selection"
  ) +
  scale_fill_discrete(
    name="Selection"
  ) +
  scale_color_discrete(
    name="Selection"
  ) + 
  theme(legend.position = "none")
final_phylogeny_fig

This shows something interesting! Final phylogenetic diversity in Eco-EA is heavily bimodal. In later analysis, we will see that the runs with high phylogenetic diversity are the ones with lower fitness, suggesting that they have no yet experienced a selective sweep resulting from the discovery of a high-fitness solution.

2.3.3.2 Mean evolutionary distinctiveness

Tests for significant differences:

# Pairwise wilcoxon teset to determine which conditions are significantly different from each other
stat.test <- final_data %>%
  wilcox_test(mean_phenotype_evolutionary_distinctiveness ~ selection_name) %>%
  adjust_pvalue(method = "bonferroni") %>%
  add_significance() %>%
  add_xy_position(x="selection_name",step.increase=1)
stat.test$label <- mapply(p_label,stat.test$p.adj)

# Output stats

stat.test %>%
  kbl() %>%
  kable_styling(
    bootstrap_options = c(
      "striped",
      "hover",
      "condensed",
      "responsive"
    )
  ) %>%
  scroll_box(width="100%")
.y. group1 group2 n1 n2 statistic p p.adj p.adj.signif y.position groups xmin xmax label
mean_phenotype_evolutionary_distinctiveness EcoEA Fitness Sharing 50 50 469 1.00e-07 7.00e-07 **** 289111.7 EcoEA , Fitness Sharing 1 2 p < 1e-04
mean_phenotype_evolutionary_distinctiveness EcoEA Lexicase 50 47 0 0.00e+00 0.00e+00 **** 449625.8 EcoEA , Lexicase 1 3 p < 1e-04
mean_phenotype_evolutionary_distinctiveness EcoEA Random 50 50 711 2.05e-04 2.05e-03 ** 610140.0 EcoEA , Random 1 4 p = 0.00205
mean_phenotype_evolutionary_distinctiveness EcoEA Tournament 50 50 569 2.70e-06 2.72e-05 **** 770654.1 EcoEA , Tournament 1 5 p < 1e-04
mean_phenotype_evolutionary_distinctiveness Fitness Sharing Lexicase 50 47 100 0.00e+00 0.00e+00 **** 931168.2 Fitness Sharing, Lexicase 2 3 p < 1e-04
mean_phenotype_evolutionary_distinctiveness Fitness Sharing Random 50 50 2428 0.00e+00 0.00e+00 **** 1091682.4 Fitness Sharing, Random 2 4 p < 1e-04
mean_phenotype_evolutionary_distinctiveness Fitness Sharing Tournament 50 50 1614 1.20e-02 1.20e-01 ns 1252196.5 Fitness Sharing, Tournament 2 5 p = 0.12
mean_phenotype_evolutionary_distinctiveness Lexicase Random 47 50 2350 0.00e+00 0.00e+00 **** 1412710.6 Lexicase, Random 3 4 p < 1e-04
mean_phenotype_evolutionary_distinctiveness Lexicase Tournament 47 50 2255 0.00e+00 0.00e+00 **** 1573224.7 Lexicase , Tournament 3 5 p < 1e-04
mean_phenotype_evolutionary_distinctiveness Random Tournament 50 50 173 0.00e+00 0.00e+00 **** 1733738.9 Random , Tournament 4 5 p < 1e-04

Looks like everything except fitness sharing and tournament are significantly different from each other.

# Raincloud plot of final mean evolutionary distinctiveness
ggplot(
    final_data,
    aes(
      x=selection_name,
      y=mean_phenotype_evolutionary_distinctiveness,
      fill=selection_name
    )
  ) +
  geom_flat_violin(
    position = position_nudge(x = .2, y = 0),
    alpha = .8,
    scale="width"
  ) +
  geom_point(
    mapping=aes(color=selection_name),
    position = position_jitter(width = .15),
    size = .5,
    alpha = 0.8
  ) +
  geom_boxplot(
    width = .1,
    outlier.shape = NA,
    alpha = 0.5
  ) +
  scale_y_log10(
    name="Mean evolutionary distinctiveness"
  ) +
  scale_x_discrete(
    name="Selection"
  ) +
  scale_fill_discrete(
    name="Selection"
  ) +
  scale_color_discrete(
    name="Selection"
  ) + 
  theme(legend.position = "none")

Again, this looks fairly similar to MPD, except that fitness sharing and tournament are higher.

2.4 Phenotypic diversity

Now we analyze phenotypic (i.e. population-level) diversity. Here, we’re defining phenotypes to only include the sites that are actively contributing to fitness. So the phenotype of [1,4,2,6,5,4,3,6,7] would be [0,0,0,6,5,4,3,0,0]. Note that phylogenetic trees are also built using this conception of phenotypes.

2.4.1 Relationship between different types of phenotypic diversity

First, we should assess the extent to which different metrics of phenotypic diversity are capturing different information.

ggplot(
    data %>% filter(gen==500000),
    aes(
        y=phen_diversity,
        x=phen_num_taxa,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Phenotypic shannon diversity"
  ) +
  scale_x_continuous(
        name="Phenotypic richness",
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

Looks like at the final time point they are pretty much always closely correlated, although this correlation is weaker for lexicase selection than for other selection schemes.

2.4.2 Over time

Now we examine the behavior of each phenotypic diversity metric over time.

2.4.2.1 Richness

ggplot(
    data,
    aes(
      x=gen,
      y=phen_num_taxa,
      color=selection_name,
      fill=selection_name
    )
  ) +
  stat_summary(geom="line", fun=mean) +
  stat_summary(
    geom="ribbon",
    fun.data="mean_cl_boot",
    fun.args=list(conf.int=0.95),
    alpha=0.2,
    linetype=0
  ) +
  scale_y_continuous(
    name="Phenotypic richness"
  ) +
  scale_x_log10(
    name="Generation"
  ) +
  scale_color_discrete("Selection") + 
  scale_fill_discrete("Selection")

In contrast to the phylodiversity results, phenotypic richness in all selection schemes (even tournament selection) ultimately exceeds that of random selection. Eco-EA monotonically increases while lexicase selection reaches a maximum around the same time it reaches its fitness plateau. The only real similarity to the phylodiversity results is the behavior tournament selection and fitness sharing relative to each other.

2.4.2.2 Shannon diversity

We also looked at phenotypic shannon diversity:

ggplot(
    data,
    aes(
      x=gen,
      y=phen_diversity,
      color=selection_name,
      fill=selection_name
    )
  ) +
  stat_summary(geom="line", fun=mean) +
  stat_summary(
    geom="ribbon",
    fun.data="mean_cl_boot",
    fun.args=list(conf.int=0.95),
    alpha=0.2,
    linetype=0
  ) +
  scale_y_continuous(
    name="Phenotypic Shannon entropy"
  ) +
  scale_x_log10(
    name="Generation"
  ) +
  scale_color_discrete("Selection") + 
  scale_fill_discrete("Selection")

This is more different from the richness results than we might have expected based on the correlation at the final time point. There is a much more pronounced drop off in Shannon entropy for tournament and fitness sharing than in richness. This difference is probably driven by the fact that, after plateauing, these selection schemes are likely both at mutation-selection balance. Thus, there are very many single-mutant phenotypes with only one copy in the population. These do not contribute much to Shannon entropy or phylogenetic diversity, but it does show up in richness.

We also see that, after plataeuing, lexicase selection does actually start to decrease in Shannon entropy, but slowly. Similarly, in Eco-EA, the increase in Shannon entropy towards the end is much more modest than the increase in richness.

2.4.3 Final

Now we assess the final phenotypic diversity

2.4.3.1 Richness

Hypothesis-testing differences between groups:

# Determine which conditions are significantly diferrent from each other
stat.test <- final_data %>%
  wilcox_test(phen_num_taxa ~ selection_name) %>%
  adjust_pvalue(method = "bonferroni") %>%
  add_significance() %>%
  add_xy_position(x="selection_name",step.increase=1)
stat.test$label <- mapply(p_label,stat.test$p.adj)

stat.test %>%
  kbl() %>%
  kable_styling(
    bootstrap_options = c(
      "striped",
      "hover",
      "condensed",
      "responsive"
    )
  ) %>%
  scroll_box(width="600px")
.y. group1 group2 n1 n2 statistic p p.adj p.adj.signif y.position groups xmin xmax label
phen_num_taxa EcoEA Fitness Sharing 50 50 2249.0 0.00e+00 0.0e+00 **** 326 EcoEA , Fitness Sharing 1 2 p < 1e-04
phen_num_taxa EcoEA Lexicase 50 47 2319.0 0.00e+00 0.0e+00 **** 456 EcoEA , Lexicase 1 3 p < 1e-04
phen_num_taxa EcoEA Random 50 50 2500.0 0.00e+00 0.0e+00 **** 586 EcoEA , Random 1 4 p < 1e-04
phen_num_taxa EcoEA Tournament 50 50 2378.5 0.00e+00 0.0e+00 **** 716 EcoEA , Tournament 1 5 p < 1e-04
phen_num_taxa Fitness Sharing Lexicase 50 47 1428.0 6.80e-02 6.8e-01 ns 846 Fitness Sharing, Lexicase 2 3 p = 0.68
phen_num_taxa Fitness Sharing Random 50 50 1973.0 6.00e-07 6.3e-06 **** 976 Fitness Sharing, Random 2 4 p < 1e-04
phen_num_taxa Fitness Sharing Tournament 50 50 1585.0 2.10e-02 2.1e-01 ns 1106 Fitness Sharing, Tournament 2 5 p = 0.21
phen_num_taxa Lexicase Random 47 50 2339.5 0.00e+00 0.0e+00 **** 1236 Lexicase, Random 3 4 p < 1e-04
phen_num_taxa Lexicase Tournament 47 50 1359.0 1.85e-01 1.0e+00 ns 1366 Lexicase , Tournament 3 5 p = 1
phen_num_taxa Random Tournament 50 50 797.0 2.00e-03 2.0e-02
1496 Random , Tournament 4 5 p = 0.02

Raincloud plot:

# Raincloud plot of final phenotypic diversity
final_phenotypic_fig <- ggplot(
    final_data,
    aes(
      x=selection_name,
      y=phen_num_taxa,
      fill=selection_name
    )
  ) +
  geom_flat_violin(
    position = position_nudge(x = .2, y = 0),
    alpha = .8,
    scale="width"
  ) +
  geom_point(
    mapping=aes(color=selection_name),
    position = position_jitter(width = .15),
    size = .5,
    alpha = 0.8
  ) +
  geom_boxplot(
    width = .1,
    outlier.shape = NA,
    alpha = 0.5
  ) +
  scale_y_continuous(
    name="Phenotypic Richness"
  ) +
  scale_x_discrete(
    name="Selection"
  ) +
  scale_fill_discrete(
    name="Selection"
  ) +
  scale_color_discrete(
    name="Selection"
  ) +
  theme(legend.position = "none")
final_phenotypic_fig

Nothing particularly suprising here, but we should note that, based on the over time plot, this would look a lot different if we had selected a different time point.

2.4.3.2 Shannon diversity

# Determine which conditions are significantly diferrent from each other
stat.test <- final_data %>%
  wilcox_test(phen_diversity~ selection_name) %>%
  adjust_pvalue(method = "bonferroni") %>%
  add_significance() %>%
  add_xy_position(x="selection_name",step.increase=1)
stat.test$label <- mapply(p_label,stat.test$p.adj)

stat.test %>%
  kbl() %>%
  kable_styling(
    bootstrap_options = c(
      "striped",
      "hover",
      "condensed",
      "responsive"
    )
  ) %>%
  scroll_box(width="600px")
.y. group1 group2 n1 n2 statistic p p.adj p.adj.signif y.position groups xmin xmax label
phen_diversity EcoEA Fitness Sharing 50 50 2478.0 0.00e+00 0.00e+00 **** 9.602 EcoEA , Fitness Sharing 1 2 p < 1e-04
phen_diversity EcoEA Lexicase 50 47 1772.0 1.66e-05 1.66e-04 *** 13.012 EcoEA , Lexicase 1 3 p = 0.000166
phen_diversity EcoEA Random 50 50 2089.5 0.00e+00 1.00e-07 **** 16.422 EcoEA , Random 1 4 p < 1e-04
phen_diversity EcoEA Tournament 50 50 2496.0 0.00e+00 0.00e+00 **** 19.832 EcoEA , Tournament 1 5 p < 1e-04
phen_diversity Fitness Sharing Lexicase 50 47 0.0 0.00e+00 0.00e+00 **** 23.242 Fitness Sharing, Lexicase 2 3 p < 1e-04
phen_diversity Fitness Sharing Random 50 50 0.0 0.00e+00 0.00e+00 **** 26.652 Fitness Sharing, Random 2 4 p < 1e-04
phen_diversity Fitness Sharing Tournament 50 50 1856.0 2.99e-05 2.99e-04 *** 30.062 Fitness Sharing, Tournament 2 5 p = 0.000299
phen_diversity Lexicase Random 47 50 1714.0 1.01e-04 1.01e-03 ** 33.472 Lexicase, Random 3 4 p = 0.00101
phen_diversity Lexicase Tournament 47 50 2350.0 0.00e+00 0.00e+00 **** 36.882 Lexicase , Tournament 3 5 p < 1e-04
phen_diversity Random Tournament 50 50 2500.0 0.00e+00 0.00e+00 **** 40.292 Random , Tournament 4 5 p < 1e-04

Interestingly, the final shannon diversity values of different selection schemes are much more distinguishable from each other than the final richness values.

# Raincloud plot of final phenotypic diversity
ggplot(
    final_data,
    aes(
      x=selection_name,
      y=phen_num_taxa,
      fill=selection_name
    )
  ) +
  geom_flat_violin(
    position = position_nudge(x = .2, y = 0),
    alpha = .8,
    scale="width"
  ) +
  geom_point(
    mapping=aes(color=selection_name),
    position = position_jitter(width = .15),
    size = .5,
    alpha = 0.8
  ) +
  geom_boxplot(
    width = .1,
    outlier.shape = NA,
    alpha = 0.5
  ) +
  scale_y_continuous(
    name="Phenotypic Shannon Diversity"
  ) +
  scale_x_discrete(
    name="Selection"
  ) +
  scale_fill_discrete(
    name="Selection"
  ) +
  scale_color_discrete(
    name="Selection"
  ) +
  theme(legend.position = "none")

Again, we know from the time series plots that these relative relationships varied a lot over time. Eco-EA is only higher than lexicase selection at the very end.

2.5 Relationship between phenotypic and phylogenetic diversity

Now, we can finally begin to address the main questions. Do phenotypic diversity and phylogenetic diversity capture different information?

2.5.1 Phenotypic richness vs. mean pairwise distance

ggplot(
    data %>% filter(gen==500000),
    aes(
        y=phen_num_taxa,
        x=mean_phenotype_pairwise_distance,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Phenotypic richness"
  ) +
  scale_x_continuous(
        name="Mean pairwise distance",
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

The linear models for some of these are questionable, but the Spearman correlation coefficient should be fine because it does not require linearity. Only tournament selection is significantly different from 0. Eco-EA is even negative (although non-significant), which is probably driven by the fact that there are really two groups of runs in Eco-EA: those that have found a good solution and had their diversity crash, and those that haven’t yet.

Lets take a look at how each of those groups behave (we’ll show fitness as color, just to better understand what’s happening):

ggplot(
    data %>% filter(gen==500000, selection_name == "EcoEA"),
    aes(
        y=phen_num_taxa,
        x=mean_phenotype_pairwise_distance,
        color = elite_trait_avg
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Phenotypic richness"
  ) +
  scale_x_continuous(
        name="Mean pairwise distance",
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~mean_phenotype_pairwise_distance > 100, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  scale_color_continuous(type="viridis")

2.5.2 Phenotypic richness vs. mean evolutionary distinctiveness

ggplot(
    data %>% filter(gen==500000),
    aes(
        y=phen_num_taxa,
        x=mean_phenotype_evolutionary_distinctiveness,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Phenotypic richness"
  ) +
  scale_x_continuous(
        name="Mean evolutionary distinctiveness",
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

These are mostly significant, but negative. That’s interesting and worthy of further exploration (but it’s a little beyond the scope of this analysis).

2.5.3 Phenotypic shannon diversity vs. mean pairwise distance

ggplot(
    data %>% filter(gen==500000),
    aes(
        y=phen_diversity,
        x=mean_phenotype_pairwise_distance,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Phenotypic shannon diversity"
  ) +
  scale_x_continuous(
        name="Mean pairwise distance",
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

Again, these are mostly not significant (although fitness sharing notably now is).

2.5.4 Phenotypic shannon diversity vs. mean evolutionary distinctiveness

ggplot(
    data %>% filter(gen==500000),
    aes(
        y=phen_diversity,
        x=mean_phenotype_evolutionary_distinctiveness,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Phenotypic shannon diversity"
  ) +
  scale_x_continuous(
        name="Mean evolutionary distinctiveness",
        breaks = breaks_extended(4)
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

Again, all significant but negative. From this we can conclude that there is substantially more overlap between the information captured by phenotypic diversity and mean evolutionary disinctiveness than between phenotypic diversity metrics and mean pairwise distance. However, thus information has wildly different implications in the two contexts. Moreover, the long term trends are still very different. Thus, we still feel confident in saying that these metrics are meaningfully different. More investigation here may be worthwhile in the future.

Overall, based on these end of time analyses and qualitative comparison of the temporal graphs, we conclude that phenotypic and phylogenetic diversity are meaningfully different from each other in the context of evolutionary computation. In retrospect, this finding may seem obvious. However a lot of people tend to assume that these two classes of diversity are closely related.

2.6 Relationship between diversity and success

At last, we can finally assess whether diversity leads to actually solving problems! We start out by looking at correlations between diversity success across a few different time points, although we ultimately conclude that this approach is not particularly informative.

2.6.1 Very early in run

2.6.1.1 Mean pairwise distance

ggplot(
    data %>% filter(gen==25000),
    aes(
        y=elite_trait_avg,
        x=mean_phenotype_pairwise_distance,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Average trait performance"
  ) +
  scale_x_continuous(
        name="Mean pairwise distance"
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

2.6.1.2 Mean evolutionary distinctiveness

ggplot(
    data %>% filter(gen==25000),
    aes(
        y=elite_trait_avg,
        x=mean_phenotype_evolutionary_distinctiveness,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Average trait performance"
  ) +
  scale_x_continuous(
        name="Mean evolutionary distinctiveness"
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

2.6.1.3 Richness

ggplot(
    data %>% filter(gen==25000),
    aes(
        y=elite_trait_avg,
        x=phen_num_taxa,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Average trait performance"
  ) +
  scale_x_continuous(
        name="Phenotypic richness"
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

2.6.1.4 Shannon diversity

ggplot(
    data %>% filter(gen==25000),
    aes(
        y=elite_trait_avg,
        x=phen_diversity,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Average trait performance"
  ) +
  scale_x_continuous(
        name="Phenotypic Shannon diversity"
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

2.6.2 Middle of run

2.6.2.1 Mean pairwise distance

ggplot(
    data %>% filter(gen==50000),
    aes(
        y=elite_trait_avg,
        x=mean_phenotype_pairwise_distance,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Average trait performance"
  ) +
  scale_x_continuous(
        name="Mean pairwise distance"
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

2.6.2.2 Mean evolutionary distinctiveness

ggplot(
    data %>% filter(gen==50000),
    aes(
        y=elite_trait_avg,
        x=mean_phenotype_evolutionary_distinctiveness,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Average trait performance"
  ) +
  scale_x_continuous(
        name="Mean evolutionary distinctiveness"
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

2.6.2.3 Richness

ggplot(
    data %>% filter(gen==50000),
    aes(
        y=elite_trait_avg,
        x=phen_num_taxa,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Average trait performance"
  ) +
  scale_x_continuous(
        name="Phenotypic richness"
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

2.6.2.4 Shannon diversity

ggplot(
    data %>% filter(gen==50000),
    aes(
        y=elite_trait_avg,
        x=phen_diversity,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Average trait performance"
  ) +
  scale_x_continuous(
        name="Phenotypic Shannon diversity"
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

2.6.3 End of run

2.6.3.1 Mean pairwise distance

ggplot(
    final_data,
    aes(
        y=elite_trait_avg,
        x=mean_phenotype_pairwise_distance,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Average trait performance"
  ) +
  scale_x_continuous(
        name="Mean pairwise distance"
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

2.6.3.2 Mean evolutionary distinctiveness

ggplot(
    final_data,
    aes(
        y=elite_trait_avg,
        x=mean_phenotype_evolutionary_distinctiveness,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Average trait performance"
  ) +
  scale_x_continuous(
        name="Mean evolutionary distinctiveness"
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

2.6.3.3 Richness

ggplot(
    final_data,
    aes(
        y=elite_trait_avg,
        x=phen_num_taxa,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Average trait performance"
  ) +
  scale_x_continuous(
        name="Phenotypic richness"
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

2.6.3.4 Shannon diversity

ggplot(
    final_data,
    aes(
        y=elite_trait_avg,
        x=phen_diversity,
        color=selection_name,
        fill=selection_name
    )
  ) +
  geom_point() +
    scale_y_continuous(
        name="Average trait performance"
  ) +
  scale_x_continuous(
        name="Phenotypic Shannon diversity"
  ) + 
  facet_wrap(
      ~selection_name, scales="free"
  ) + 
  stat_smooth(
    method="lm"
  ) + 
  stat_cor(
    method="spearman", cor.coef.name = "rho", color="black"
  ) +
  theme(legend.position = "none")

2.7 Causality analysis

Ultimately, it’s hard to draw useful inferences either from these single time point analyses or from comparison of line plots. Due to the feedbacks between diversity and performance, there may be a time delay in when one affects the other.

To analyze the drivers of this feedback loop in a more rigorous way, we turn to Transfer Entropy as a metric of Granger Causality. For a more thorough description, see the paper associated with this supplement.

2.7.1 Setup

First let’s define a function we’ll use to calculate and output significance and effect size for these results:

transfer_entropy_stats <- function(res) {
  stat.test <- res %>%
    group_by(selection_name, offset) %>%
    wilcox_test(value ~ Type) %>%
    adjust_pvalue(method = "bonferroni") %>%
    add_significance() 
  stat.test$label <- mapply(p_label,stat.test$p.adj)
  
  # Calculate effect sizes for these differences
  effect_sizes <- res %>%
    group_by(selection_name, offset) %>%
    wilcox_effsize(value ~ Type)
  
  stat.test$effsize <- effect_sizes$effsize
  stat.test$magnitude <- effect_sizes$magnitude
  
  stat.test %>%
    kbl() %>%
    kable_styling(
      bootstrap_options = c(
        "striped",
        "hover",
        "condensed",
        "responsive"
      )
    ) %>%
    scroll_box(width="600px")
}

2.7.2 Transfer entropy from diversity to fitness

First, we calculate the information that each type of diversity gives us about future fitness.

2.7.2.1 Max pairwise distance vs. phenotypic richness

We plot the differences in transfer entropy from phylogenetic diversity to fitness and from phenotypic diversity to fitness, at a range of different lags.

# Calculate transfer entropy for max pairwise distance
# Time points are 10 generations, so calculating lag 1 gives us lag 10
res <- data %>% group_by(directory, selection_name) %>%
summarise(
  fit_phylo_10 =     condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 10
                                     discretize(lag(max_phenotype_pairwise_distance, 1)), 
                                     discretize(lag(elite_trait_avg, 1))),
  fit_phylo_100 =    condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 100
                                     discretize(lag(max_phenotype_pairwise_distance, 10)), 
                                     discretize(lag(elite_trait_avg, 10))),
  fit_phylo_1000 =   condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 1000
                                     discretize(lag(max_phenotype_pairwise_distance, 100)), 
                                     discretize(lag(elite_trait_avg, 100))),
  fit_phylo_10000 =  condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 10000
                                     discretize(lag(max_phenotype_pairwise_distance, 1000)), 
                                     discretize(lag(elite_trait_avg, 1000))),
  fit_phylo_100000 = condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 100000
                                     discretize(lag(max_phenotype_pairwise_distance, 10000)), 
                                     discretize(lag(elite_trait_avg, 10000))),  
  fit_fit_10000 =    condinformation(discretize(elite_trait_avg), # Mutual info btwn. fit and lagged fit
                                     discretize(lag(elite_trait_avg, 1000)), 
                                     discretize(lag(max_phenotype_pairwise_distance, 1000))),
  fit_fit_100000 =   condinformation(discretize(elite_trait_avg), # Mutual info btwn. fit and lagged fit
                                     discretize(lag(elite_trait_avg, 10000)), 
                                     discretize(lag(max_phenotype_pairwise_distance, 10000))),  
  fit_pheno_10 =     condinformation(discretize(elite_trait_avg), # TE pheno -> fit, lag 10
                                     discretize(lag(phen_num_taxa, 1)), 
                                     discretize(lag(elite_trait_avg, 1))),
  fit_pheno_100 =    condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 100 
                                     discretize(lag(phen_num_taxa, 10)), 
                                     discretize(lag(elite_trait_avg, 10))),
  fit_pheno_1000 =   condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 1000
                                     discretize(lag(phen_num_taxa, 100)), 
                                     discretize(lag(elite_trait_avg, 100))),
  fit_pheno_10000 =  condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 10000
                                     discretize(lag(phen_num_taxa, 1000)), 
                                     discretize(lag(elite_trait_avg, 1000))),
  fit_pheno_100000 = condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 100000
                                     discretize(lag(phen_num_taxa, 10000)), 
                                     discretize(lag(elite_trait_avg, 10000)))
)

# Turn Transfer Entropy columns into rows
res <- res %>% pivot_longer(cols=contains("o_10"))
# Pull lag into a column
res$offset <- str_extract(res$name, "[:digit:]*$")
# Make column indicating direction of transfer entropy
res$Type <- case_when(str_detect(res$name, "phylo") ~ "Phylogenetic", TRUE ~ "Phenotypic")

# Plot transfer entropy
ggplot(
  res %>% filter(str_detect(name, "fit_ph*")), 
  aes(
    x=as.factor(offset), 
    y=value, 
    color=Type
    )
  ) + 
  geom_boxplot() + 
  facet_wrap(~selection_name) + 
  scale_x_discrete("Lag",labels=c("10","","1000","","100000")) + 
  scale_y_continuous("Transfer Entropy") + 
  theme(legend.position = c(1, 0),
        legend.justification = c(1, 0)) + 
  scale_color_discrete("")

Next we calculate statistics to quantify these differences

# Determine which conditions are significantly different from each other
transfer_entropy_stats(res)
selection_name offset .y. group1 group2 n1 n2 statistic p p.adj p.adj.signif label effsize magnitude
EcoEA 10 value Phenotypic Phylogenetic 50 50 466 1.00e-07 0.0000017 **** p < 1e-04 0.5404755 large
EcoEA 100 value Phenotypic Phylogenetic 50 50 234 0.00e+00 0.0000000 **** p < 1e-04 0.7004121 large
EcoEA 1000 value Phenotypic Phylogenetic 50 50 192 0.00e+00 0.0000000 **** p < 1e-04 0.7293661 large
EcoEA 10000 value Phenotypic Phylogenetic 50 50 165 0.00e+00 0.0000000 **** p < 1e-04 0.7479795 large
EcoEA 100000 value Phenotypic Phylogenetic 50 50 242 0.00e+00 0.0000000 **** p < 1e-04 0.6948970 large
Fitness Sharing 10 value Phenotypic Phylogenetic 50 50 798 1.85e-03 0.0462500
p = 0.04625 0.3116007 moderate
Fitness Sharing 100 value Phenotypic Phylogenetic 50 50 759 7.21e-04 0.0180250
p = 0.018025 0.3384866 moderate
Fitness Sharing 1000 value Phenotypic Phylogenetic 50 50 826 3.51e-03 0.0877500 ns p = 0.08775 0.2922980 small
Fitness Sharing 10000 value Phenotypic Phylogenetic 50 50 661 4.97e-05 0.0012425 ** p = 0.0012425 0.4060460 moderate
Fitness Sharing 100000 value Phenotypic Phylogenetic 50 50 436 0.00e+00 0.0000005 **** p < 1e-04 0.5611569 large
Lexicase 10 value Phenotypic Phylogenetic 50 50 195 0.00e+00 0.0000000 **** p < 1e-04 0.7272980 large
Lexicase 100 value Phenotypic Phylogenetic 50 50 173 0.00e+00 0.0000000 **** p < 1e-04 0.7424644 large
Lexicase 1000 value Phenotypic Phylogenetic 50 50 34 0.00e+00 0.0000000 **** p < 1e-04 0.8382885 large
Lexicase 10000 value Phenotypic Phylogenetic 50 50 6 0.00e+00 0.0000000 **** p < 1e-04 0.8575912 large
Lexicase 100000 value Phenotypic Phylogenetic 50 50 1 0.00e+00 0.0000000 **** p < 1e-04 0.8610381 large
Random 10 value Phenotypic Phylogenetic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 100 value Phenotypic Phylogenetic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 1000 value Phenotypic Phylogenetic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 10000 value Phenotypic Phylogenetic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 100000 value Phenotypic Phylogenetic 50 50 358 0.00e+00 0.0000000 **** p < 1e-04 0.6149287 large
Tournament 10 value Phenotypic Phylogenetic 50 50 1810 1.15e-04 0.0028750 ** p = 0.002875 0.3860539 moderate
Tournament 100 value Phenotypic Phylogenetic 50 50 2024 1.00e-07 0.0000024 **** p < 1e-04 0.5335817 large
Tournament 1000 value Phenotypic Phylogenetic 50 50 2165 0.00e+00 0.0000000 **** p < 1e-04 0.6307845 large
Tournament 10000 value Phenotypic Phylogenetic 50 50 2322 0.00e+00 0.0000000 **** p < 1e-04 0.7390175 large
Tournament 100000 value Phenotypic Phylogenetic 50 50 1336 5.56e-01 1.0000000 ns p = 1 0.0592869 small

2.7.2.2 Mean pairwise distance vs. phenotypic richness

# Calculate transfer entropy for mean pairwise distance
# Time points are 10 generations, so calculating lag 1 gives us lag 10
res <- data %>% group_by(directory, selection_name) %>%
summarise(
  fit_phylo_10 =     condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 10
                                     discretize(lag(mean_phenotype_pairwise_distance, 1)), 
                                     discretize(lag(elite_trait_avg, 1))),
  fit_phylo_100 =    condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 100
                                     discretize(lag(mean_phenotype_pairwise_distance, 10)), 
                                     discretize(lag(elite_trait_avg, 10))),
  fit_phylo_1000 =   condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 1000
                                     discretize(lag(mean_phenotype_pairwise_distance, 100)), 
                                     discretize(lag(elite_trait_avg, 100))),
  fit_phylo_10000 =  condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 10000
                                     discretize(lag(mean_phenotype_pairwise_distance, 1000)), 
                                     discretize(lag(elite_trait_avg, 1000))),
  fit_phylo_100000 = condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 100000
                                     discretize(lag(mean_phenotype_pairwise_distance, 10000)), 
                                     discretize(lag(elite_trait_avg, 10000))),  
  fit_fit_10000 =    condinformation(discretize(elite_trait_avg), # Mutual info btwn. fit and lagged fit
                                     discretize(lag(elite_trait_avg, 1000)), 
                                     discretize(lag(mean_phenotype_pairwise_distance, 1000))),
  fit_fit_100000 =   condinformation(discretize(elite_trait_avg), # Mutual info btwn. fit and lagged fit
                                     discretize(lag(elite_trait_avg, 10000)), 
                                     discretize(lag(mean_phenotype_pairwise_distance, 10000))),  
  fit_pheno_10 =     condinformation(discretize(elite_trait_avg), # TE pheno -> fit, lag 10
                                     discretize(lag(phen_num_taxa, 1)), 
                                     discretize(lag(elite_trait_avg, 1))),
  fit_pheno_100 =    condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 100 
                                     discretize(lag(phen_num_taxa, 10)), 
                                     discretize(lag(elite_trait_avg, 10))),
  fit_pheno_1000 =   condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 1000
                                     discretize(lag(phen_num_taxa, 100)), 
                                     discretize(lag(elite_trait_avg, 100))),
  fit_pheno_10000 =  condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 10000
                                     discretize(lag(phen_num_taxa, 1000)), 
                                     discretize(lag(elite_trait_avg, 1000))),
  fit_pheno_100000 = condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 100000
                                     discretize(lag(phen_num_taxa, 10000)), 
                                     discretize(lag(elite_trait_avg, 10000)))
)

# Turn Transfer Entropy columns into rows
res <- res %>% pivot_longer(cols=contains("o_10"))
# Pull lag into a column
res$offset <- str_extract(res$name, "[:digit:]*$")
# Make column indicating direction of transfer entropy
res$Type <- case_when(str_detect(res$name, "phylo") ~ "Phylogenetic", TRUE ~ "Phenotypic")

# Plot transfer entropy
ggplot(
  res %>% filter(str_detect(name, "fit_ph*")), 
  aes(
    x=as.factor(offset), 
    y=value, 
    color=Type
    )
  ) + 
  geom_boxplot() + 
  facet_wrap(~selection_name) + 
  scale_x_discrete("Lag",labels=c("10","","1000","","100000")) + 
  scale_y_continuous("Transfer Entropy") + 
  theme(legend.position = c(1, 0),
        legend.justification = c(1, 0)) + 
  scale_color_discrete("")

Mean and max pairwise distance appear to behave virtually identically.

# Determine which conditions are significantly different from each other
transfer_entropy_stats(res)
selection_name offset .y. group1 group2 n1 n2 statistic p p.adj p.adj.signif label effsize magnitude
EcoEA 10 value Phenotypic Phylogenetic 50 50 288 0.00e+00 0.0000000 **** p < 1e-04 0.6631855 large
EcoEA 100 value Phenotypic Phylogenetic 50 50 256 0.00e+00 0.0000000 **** p < 1e-04 0.6852457 large
EcoEA 1000 value Phenotypic Phylogenetic 50 50 330 0.00e+00 0.0000000 **** p < 1e-04 0.6342314 large
EcoEA 10000 value Phenotypic Phylogenetic 50 50 209 0.00e+00 0.0000000 **** p < 1e-04 0.7176466 large
EcoEA 100000 value Phenotypic Phylogenetic 50 50 381 0.00e+00 0.0000001 **** p < 1e-04 0.5990729 large
Fitness Sharing 10 value Phenotypic Phylogenetic 50 50 521 5.00e-07 0.0000128 **** p < 1e-04 0.5025595 large
Fitness Sharing 100 value Phenotypic Phylogenetic 50 50 550 1.40e-06 0.0000355 **** p < 1e-04 0.4825674 moderate
Fitness Sharing 1000 value Phenotypic Phylogenetic 50 50 612 1.11e-05 0.0002775 *** p = 0.0002775 0.4398257 moderate
Fitness Sharing 10000 value Phenotypic Phylogenetic 50 50 459 1.00e-07 0.0000013 **** p < 1e-04 0.5453012 large
Fitness Sharing 100000 value Phenotypic Phylogenetic 50 50 350 0.00e+00 0.0000000 **** p < 1e-04 0.6204438 large
Lexicase 10 value Phenotypic Phylogenetic 50 50 195 0.00e+00 0.0000000 **** p < 1e-04 0.7272980 large
Lexicase 100 value Phenotypic Phylogenetic 50 50 220 0.00e+00 0.0000000 **** p < 1e-04 0.7100634 large
Lexicase 1000 value Phenotypic Phylogenetic 50 50 333 0.00e+00 0.0000000 **** p < 1e-04 0.6321633 large
Lexicase 10000 value Phenotypic Phylogenetic 50 50 142 0.00e+00 0.0000000 **** p < 1e-04 0.7638352 large
Lexicase 100000 value Phenotypic Phylogenetic 50 50 115 0.00e+00 0.0000000 **** p < 1e-04 0.7824486 large
Random 10 value Phenotypic Phylogenetic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 100 value Phenotypic Phylogenetic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 1000 value Phenotypic Phylogenetic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 10000 value Phenotypic Phylogenetic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 100000 value Phenotypic Phylogenetic 50 50 538 9.00e-07 0.0000233 **** p < 1e-04 0.4908400 moderate
Tournament 10 value Phenotypic Phylogenetic 50 50 928 2.67e-02 0.6675000 ns p = 0.6675 0.2219810 small
Tournament 100 value Phenotypic Phylogenetic 50 50 1105 3.19e-01 1.0000000 ns p = 1 0.0999604 small
Tournament 1000 value Phenotypic Phylogenetic 50 50 1184 6.52e-01 1.0000000 ns p = 1 0.0454992 small
Tournament 10000 value Phenotypic Phylogenetic 50 50 1188 6.72e-01 1.0000000 ns p = 1 0.0427417 small
Tournament 100000 value Phenotypic Phylogenetic 50 50 618 1.34e-05 0.0003350 *** p = 0.000335 0.4356894 moderate

2.7.2.3 Mean pairwise distance vs. shannon entropy

# Calculate transfer entropy for mean pairwise distance
# Time points are 10 generations, so calculating lag 1 gives us lag 10
res <- data %>% group_by(directory, selection_name) %>%
summarise(
  fit_phylo_10 =     condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 10
                                     discretize(lag(mean_phenotype_pairwise_distance, 1)), 
                                     discretize(lag(elite_trait_avg, 1))),
  fit_phylo_100 =    condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 100
                                     discretize(lag(mean_phenotype_pairwise_distance, 10)), 
                                     discretize(lag(elite_trait_avg, 10))),
  fit_phylo_1000 =   condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 1000
                                     discretize(lag(mean_phenotype_pairwise_distance, 100)), 
                                     discretize(lag(elite_trait_avg, 100))),
  fit_phylo_10000 =  condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 10000
                                     discretize(lag(mean_phenotype_pairwise_distance, 1000)), 
                                     discretize(lag(elite_trait_avg, 1000))),
  fit_phylo_100000 = condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 100000
                                     discretize(lag(mean_phenotype_pairwise_distance, 10000)), 
                                     discretize(lag(elite_trait_avg, 10000))),  
  fit_fit_10000 =    condinformation(discretize(elite_trait_avg), # Mutual info btwn. fit and lagged fit
                                     discretize(lag(elite_trait_avg, 1000)), 
                                     discretize(lag(mean_phenotype_pairwise_distance, 1000))),
  fit_fit_100000 =   condinformation(discretize(elite_trait_avg), # Mutual info btwn. fit and lagged fit
                                     discretize(lag(elite_trait_avg, 10000)), 
                                     discretize(lag(mean_phenotype_pairwise_distance, 10000))),  
  fit_pheno_10 =     condinformation(discretize(elite_trait_avg), # TE pheno -> fit, lag 10
                                     discretize(lag(phen_diversity, 1)), 
                                     discretize(lag(elite_trait_avg, 1))),
  fit_pheno_100 =    condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 100 
                                     discretize(lag(phen_diversity, 10)), 
                                     discretize(lag(elite_trait_avg, 10))),
  fit_pheno_1000 =   condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 1000
                                     discretize(lag(phen_diversity, 100)), 
                                     discretize(lag(elite_trait_avg, 100))),
  fit_pheno_10000 =  condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 10000
                                     discretize(lag(phen_diversity, 1000)), 
                                     discretize(lag(elite_trait_avg, 1000))),
  fit_pheno_100000 = condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 100000
                                     discretize(lag(phen_diversity, 10000)), 
                                     discretize(lag(elite_trait_avg, 10000)))
)

# Turn Transfer Entropy columns into rows
res <- res %>% pivot_longer(cols=contains("o_10"))
# Pull lag into a column
res$offset <- str_extract(res$name, "[:digit:]*$")
# Make column indicating direction of transfer entropy
res$Type <- case_when(str_detect(res$name, "phylo") ~ "Phylogenetic", TRUE ~ "Phenotypic")

# Plot transfer entropy
ggplot(
  res %>% filter(str_detect(name, "fit_ph*")), 
  aes(
    x=as.factor(offset), 
    y=value, 
    color=Type
    )
  ) + 
  geom_boxplot() + 
  facet_wrap(~selection_name) + 
  scale_x_discrete("Lag",labels=c("10","","1000","","100000")) + 
  scale_y_continuous("Transfer Entropy") + 
  theme(legend.position = c(1, 0),
        legend.justification = c(1, 0)) + 
  scale_color_discrete("")

Looks like Shannon Diversity is more predictive of fitness than phenotypic richness (at least for lexicase), although still not as much as mean pairwise distance.

# Determine which conditions are significantly different from each other
transfer_entropy_stats(res)
selection_name offset .y. group1 group2 n1 n2 statistic p p.adj p.adj.signif label effsize magnitude
EcoEA 10 value Phenotypic Phylogenetic 50 50 789 1.50e-03 0.0375000
p = 0.0375 0.3178051 moderate
EcoEA 100 value Phenotypic Phylogenetic 50 50 666 5.76e-05 0.0014400 ** p = 0.00144 0.4025991 moderate
EcoEA 1000 value Phenotypic Phylogenetic 50 50 678 8.15e-05 0.0020375 ** p = 0.0020375 0.3943265 moderate
EcoEA 10000 value Phenotypic Phylogenetic 50 50 291 0.00e+00 0.0000000 **** p < 1e-04 0.6611173 large
EcoEA 100000 value Phenotypic Phylogenetic 50 50 445 0.00e+00 0.0000007 **** p < 1e-04 0.5549525 large
Fitness Sharing 10 value Phenotypic Phylogenetic 50 50 706 1.79e-04 0.0044750 ** p = 0.004475 0.3750238 moderate
Fitness Sharing 100 value Phenotypic Phylogenetic 50 50 726 3.07e-04 0.0076750 ** p = 0.007675 0.3612362 moderate
Fitness Sharing 1000 value Phenotypic Phylogenetic 50 50 781 1.24e-03 0.0310000
p = 0.031 0.3233202 moderate
Fitness Sharing 10000 value Phenotypic Phylogenetic 50 50 617 1.30e-05 0.0003250 *** p = 0.000325 0.4363788 moderate
Fitness Sharing 100000 value Phenotypic Phylogenetic 50 50 538 9.00e-07 0.0000233 **** p < 1e-04 0.4908400 moderate
Lexicase 10 value Phenotypic Phylogenetic 50 50 561 2.10e-06 0.0000518 **** p < 1e-04 0.4749842 moderate
Lexicase 100 value Phenotypic Phylogenetic 50 50 612 1.11e-05 0.0002775 *** p = 0.0002775 0.4398257 moderate
Lexicase 1000 value Phenotypic Phylogenetic 50 50 829 3.75e-03 0.0937500 ns p = 0.09375 0.2902298 small
Lexicase 10000 value Phenotypic Phylogenetic 50 50 610 1.04e-05 0.0002600 *** p = 0.00026 0.4412045 moderate
Lexicase 100000 value Phenotypic Phylogenetic 50 50 611 1.07e-05 0.0002675 *** p = 0.0002675 0.4405151 moderate
Random 10 value Phenotypic Phylogenetic 50 50 404 0.00e+00 0.0000001 **** p < 1e-04 0.5832172 large
Random 100 value Phenotypic Phylogenetic 50 50 1 0.00e+00 0.0000000 **** p < 1e-04 0.8610381 large
Random 1000 value Phenotypic Phylogenetic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 10000 value Phenotypic Phylogenetic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 100000 value Phenotypic Phylogenetic 50 50 580 3.90e-06 0.0000980 **** p < 1e-04 0.4618859 moderate
Tournament 10 value Phenotypic Phylogenetic 50 50 1241 9.53e-01 1.0000000 ns p = 1 0.0062044 small
Tournament 100 value Phenotypic Phylogenetic 50 50 1225 8.66e-01 1.0000000 ns p = 1 0.0172345 small
Tournament 1000 value Phenotypic Phylogenetic 50 50 1265 9.20e-01 1.0000000 ns p = 1 0.0103407 small
Tournament 10000 value Phenotypic Phylogenetic 50 50 1217 8.23e-01 1.0000000 ns p = 1 0.0227496 small
Tournament 100000 value Phenotypic Phylogenetic 50 50 1105 3.19e-01 1.0000000 ns p = 1 0.0999604 small

2.7.2.4 Mean evolutionary distinctiveness vs. phenotypic richness

# Calculate transfer entropy for mean evolutionary distinctiveness
# Time points are 10 generations, so calculating lag 1 gives us lag 10
res <- data %>% group_by(directory, selection_name) %>%
summarise(
  fit_phylo_10 =     condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 10
                                     discretize(lag(mean_phenotype_evolutionary_distinctiveness, 1)), 
                                     discretize(lag(elite_trait_avg, 1))),
  fit_phylo_100 =    condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 100
                                     discretize(lag(mean_phenotype_evolutionary_distinctiveness, 10)), 
                                     discretize(lag(elite_trait_avg, 10))),
  fit_phylo_1000 =   condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 1000
                                     discretize(lag(mean_phenotype_evolutionary_distinctiveness, 100)), 
                                     discretize(lag(elite_trait_avg, 100))),
  fit_phylo_10000 =  condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 10000
                                     discretize(lag(mean_phenotype_evolutionary_distinctiveness, 1000)), 
                                     discretize(lag(elite_trait_avg, 1000))),
  fit_phylo_100000 = condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 100000
                                     discretize(lag(mean_phenotype_evolutionary_distinctiveness, 10000)), 
                                     discretize(lag(elite_trait_avg, 10000))),  
  fit_fit_10000 =    condinformation(discretize(elite_trait_avg), # Mutual info btwn. fit and lagged fit
                                     discretize(lag(elite_trait_avg, 1000)), 
                                     discretize(lag(mean_phenotype_evolutionary_distinctiveness, 1000))),
  fit_fit_100000 =   condinformation(discretize(elite_trait_avg), # Mutual info btwn. fit and lagged fit
                                     discretize(lag(elite_trait_avg, 10000)), 
                                     discretize(lag(mean_phenotype_evolutionary_distinctiveness, 10000))),  
  fit_pheno_10 =     condinformation(discretize(elite_trait_avg), # TE pheno -> fit, lag 10
                                     discretize(lag(phen_num_taxa, 1)), 
                                     discretize(lag(elite_trait_avg, 1))),
  fit_pheno_100 =    condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 100 
                                     discretize(lag(phen_num_taxa, 10)), 
                                     discretize(lag(elite_trait_avg, 10))),
  fit_pheno_1000 =   condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 1000
                                     discretize(lag(phen_num_taxa, 100)), 
                                     discretize(lag(elite_trait_avg, 100))),
  fit_pheno_10000 =  condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 10000
                                     discretize(lag(phen_num_taxa, 1000)), 
                                     discretize(lag(elite_trait_avg, 1000))),
  fit_pheno_100000 = condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 100000
                                     discretize(lag(phen_num_taxa, 10000)), 
                                     discretize(lag(elite_trait_avg, 10000)))
)

# Turn Transfer Entropy columns into rows
res <- res %>% pivot_longer(cols=contains("o_10"))
# Pull lag into a column
res$offset <- str_extract(res$name, "[:digit:]*$")
# Make column indicating direction of transfer entropy
res$Type <- case_when(str_detect(res$name, "phylo") ~ "Phylogenetic", TRUE ~ "Phenotypic")

# Plot transfer entropy
ggplot(
  res %>% filter(str_detect(name, "fit_ph*")), 
  aes(
    x=as.factor(offset), 
    y=value, 
    color=Type
    )
  ) + 
  geom_boxplot() + 
  facet_wrap(~selection_name) + 
  scale_x_discrete("Lag",labels=c("10","","1000","","100000")) + 
  scale_y_continuous("Transfer Entropy") + 
  theme(legend.position = c(1, 0),
        legend.justification = c(1, 0)) + 
  scale_color_discrete("")

Interestingly, mean evolutionary distinctiveness looks fairly comparable to mean pairwise distance (although note that the y axis doesn’t go quite as far).

# Determine which conditions are significantly different from each other
transfer_entropy_stats(res)
selection_name offset .y. group1 group2 n1 n2 statistic p p.adj p.adj.signif label effsize magnitude
EcoEA 10 value Phenotypic Phylogenetic 50 50 543 1.10e-06 0.0000278 **** p < 1e-04 0.4873931 moderate
EcoEA 100 value Phenotypic Phylogenetic 50 50 536 9.00e-07 0.0000218 **** p < 1e-04 0.4922187 moderate
EcoEA 1000 value Phenotypic Phylogenetic 50 50 644 2.99e-05 0.0007475 *** p = 0.0007475 0.4177655 moderate
EcoEA 10000 value Phenotypic Phylogenetic 50 50 380 0.00e+00 0.0000001 **** p < 1e-04 0.5997623 large
EcoEA 100000 value Phenotypic Phylogenetic 50 50 501 2.00e-07 0.0000062 **** p < 1e-04 0.5163471 large
Fitness Sharing 10 value Phenotypic Phylogenetic 50 50 531 7.00e-07 0.0000182 **** p < 1e-04 0.4956656 moderate
Fitness Sharing 100 value Phenotypic Phylogenetic 50 50 551 1.50e-06 0.0000368 **** p < 1e-04 0.4818780 moderate
Fitness Sharing 1000 value Phenotypic Phylogenetic 50 50 423 0.00e+00 0.0000003 **** p < 1e-04 0.5701189 large
Fitness Sharing 10000 value Phenotypic Phylogenetic 50 50 321 0.00e+00 0.0000000 **** p < 1e-04 0.6404359 large
Fitness Sharing 100000 value Phenotypic Phylogenetic 50 50 107 0.00e+00 0.0000000 **** p < 1e-04 0.7879636 large
Lexicase 10 value Phenotypic Phylogenetic 50 50 237 0.00e+00 0.0000000 **** p < 1e-04 0.6983440 large
Lexicase 100 value Phenotypic Phylogenetic 50 50 275 0.00e+00 0.0000000 **** p < 1e-04 0.6721474 large
Lexicase 1000 value Phenotypic Phylogenetic 50 50 516 4.00e-07 0.0000107 **** p < 1e-04 0.5060064 large
Lexicase 10000 value Phenotypic Phylogenetic 50 50 220 0.00e+00 0.0000000 **** p < 1e-04 0.7100634 large
Lexicase 100000 value Phenotypic Phylogenetic 50 50 178 0.00e+00 0.0000000 **** p < 1e-04 0.7390175 large
Random 10 value Phenotypic Phylogenetic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 100 value Phenotypic Phylogenetic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 1000 value Phenotypic Phylogenetic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 10000 value Phenotypic Phylogenetic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 100000 value Phenotypic Phylogenetic 50 50 893 1.40e-02 0.3500000 ns p = 0.35 0.2461094 small
Tournament 10 value Phenotypic Phylogenetic 50 50 654 4.04e-05 0.0010100 ** p = 0.00101 0.4108717 moderate
Tournament 100 value Phenotypic Phylogenetic 50 50 718 2.48e-04 0.0062000 ** p = 0.0062 0.3667512 moderate
Tournament 1000 value Phenotypic Phylogenetic 50 50 735 3.90e-04 0.0097500 ** p = 0.00975 0.3550317 moderate
Tournament 10000 value Phenotypic Phylogenetic 50 50 180 0.00e+00 0.0000000 **** p < 1e-04 0.7376387 large
Tournament 100000 value Phenotypic Phylogenetic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large

2.7.2.5 Mean evolutionary distinctiveness vs. shannon entropy

# Calculate transfer entropy for mean evolutionary distinctiveness
# Time points are 10 generations, so calculating lag 1 gives us lag 10
res <- data %>% group_by(directory, selection_name) %>%
summarise(
  fit_phylo_10 =     condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 10
                                     discretize(lag(mean_phenotype_evolutionary_distinctiveness, 1)), 
                                     discretize(lag(elite_trait_avg, 1))),
  fit_phylo_100 =    condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 100
                                     discretize(lag(mean_phenotype_evolutionary_distinctiveness, 10)), 
                                     discretize(lag(elite_trait_avg, 10))),
  fit_phylo_1000 =   condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 1000
                                     discretize(lag(mean_phenotype_evolutionary_distinctiveness, 100)), 
                                     discretize(lag(elite_trait_avg, 100))),
  fit_phylo_10000 =  condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 10000
                                     discretize(lag(mean_phenotype_evolutionary_distinctiveness, 1000)), 
                                     discretize(lag(elite_trait_avg, 1000))),
  fit_phylo_100000 = condinformation(discretize(elite_trait_avg), # TE phylo -> fit, lag 100000
                                     discretize(lag(mean_phenotype_evolutionary_distinctiveness, 10000)), 
                                     discretize(lag(elite_trait_avg, 10000))),  
  fit_fit_10000 =    condinformation(discretize(elite_trait_avg), # Mutual info btwn. fit and lagged fit
                                     discretize(lag(elite_trait_avg, 1000)), 
                                     discretize(lag(mean_phenotype_evolutionary_distinctiveness, 1000))),
  fit_fit_100000 =   condinformation(discretize(elite_trait_avg), # Mutual info btwn. fit and lagged fit
                                     discretize(lag(elite_trait_avg, 10000)), 
                                     discretize(lag(mean_phenotype_evolutionary_distinctiveness, 10000))),  
  fit_pheno_10 =     condinformation(discretize(elite_trait_avg), # TE pheno -> fit, lag 10
                                     discretize(lag(phen_diversity, 1)), 
                                     discretize(lag(elite_trait_avg, 1))),
  fit_pheno_100 =    condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 100 
                                     discretize(lag(phen_diversity, 10)), 
                                     discretize(lag(elite_trait_avg, 10))),
  fit_pheno_1000 =   condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 1000
                                     discretize(lag(phen_diversity, 100)), 
                                     discretize(lag(elite_trait_avg, 100))),
  fit_pheno_10000 =  condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 10000
                                     discretize(lag(phen_diversity, 1000)), 
                                     discretize(lag(elite_trait_avg, 1000))),
  fit_pheno_100000 = condinformation(discretize(elite_trait_avg),  # TE pheno -> fit, lag 100000
                                     discretize(lag(phen_diversity, 10000)), 
                                     discretize(lag(elite_trait_avg, 10000)))
)

# Turn Transfer Entropy columns into rows
res <- res %>% pivot_longer(cols=contains("o_10"))
# Pull lag into a column
res$offset <- str_extract(res$name, "[:digit:]*$")
# Make column indicating direction of transfer entropy
res$Type <- case_when(str_detect(res$name, "phylo") ~ "Phylogenetic", TRUE ~ "Phenotypic")

# Plot transfer entropy
ggplot(
  res %>% filter(str_detect(name, "fit_ph*")), 
  aes(
    x=as.factor(offset), 
    y=value, 
    color=Type
    )
  ) + 
  geom_boxplot() + 
  facet_wrap(~selection_name) + 
  scale_x_discrete("Lag",labels=c("10","","1000","","100000")) + 
  scale_y_continuous("Transfer Entropy") + 
  theme(legend.position = c(1, 0),
        legend.justification = c(1, 0)) + 
  scale_color_discrete("")

# Determine which conditions are significantly different from each other
transfer_entropy_stats(res)
selection_name offset .y. group1 group2 n1 n2 statistic p p.adj p.adj.signif label effsize magnitude
EcoEA 10 value Phenotypic Phylogenetic 50 50 1381 3.68e-01 1.0000000 ns p = 1 0.0903090 small
EcoEA 100 value Phenotypic Phylogenetic 50 50 1356 4.67e-01 1.0000000 ns p = 1 0.0730745 small
EcoEA 1000 value Phenotypic Phylogenetic 50 50 1345 5.15e-01 1.0000000 ns p = 1 0.0654913 small
EcoEA 10000 value Phenotypic Phylogenetic 50 50 527 6.00e-07 0.0000158 **** p < 1e-04 0.4984232 moderate
EcoEA 100000 value Phenotypic Phylogenetic 50 50 585 4.60e-06 0.0001158 *** p = 0.00011575 0.4584390 moderate
Fitness Sharing 10 value Phenotypic Phylogenetic 50 50 816 2.80e-03 0.0700000 ns p = 0.07 0.2991918 small
Fitness Sharing 100 value Phenotypic Phylogenetic 50 50 839 4.66e-03 0.1165000 ns p = 0.1165 0.2833360 small
Fitness Sharing 1000 value Phenotypic Phylogenetic 50 50 620 1.43e-05 0.0003575 *** p = 0.0003575 0.4343107 moderate
Fitness Sharing 10000 value Phenotypic Phylogenetic 50 50 452 0.00e+00 0.0000010 **** p < 1e-04 0.5501268 large
Fitness Sharing 100000 value Phenotypic Phylogenetic 50 50 328 0.00e+00 0.0000000 **** p < 1e-04 0.6356102 large
Lexicase 10 value Phenotypic Phylogenetic 50 50 644 2.99e-05 0.0007475 *** p = 0.0007475 0.4177655 moderate
Lexicase 100 value Phenotypic Phylogenetic 50 50 700 1.52e-04 0.0038000 ** p = 0.0038 0.3791601 moderate
Lexicase 1000 value Phenotypic Phylogenetic 50 50 1043 1.55e-01 1.0000000 ns p = 1 0.1427021 small
Lexicase 10000 value Phenotypic Phylogenetic 50 50 766 8.59e-04 0.0214750
p = 0.021475 0.3336609 moderate
Lexicase 100000 value Phenotypic Phylogenetic 50 50 693 1.25e-04 0.0031250 ** p = 0.003125 0.3839858 moderate
Random 10 value Phenotypic Phylogenetic 50 50 718 2.48e-04 0.0062000 ** p = 0.0062 0.3667512 moderate
Random 100 value Phenotypic Phylogenetic 50 50 271 0.00e+00 0.0000000 **** p < 1e-04 0.6749050 large
Random 1000 value Phenotypic Phylogenetic 50 50 72 0.00e+00 0.0000000 **** p < 1e-04 0.8120920 large
Random 10000 value Phenotypic Phylogenetic 50 50 130 0.00e+00 0.0000000 **** p < 1e-04 0.7721078 large
Random 100000 value Phenotypic Phylogenetic 50 50 957 4.38e-02 1.0000000 ns p = 1 0.2019889 small
Tournament 10 value Phenotypic Phylogenetic 50 50 1034 1.37e-01 1.0000000 ns p = 1 0.1489065 small
Tournament 100 value Phenotypic Phylogenetic 50 50 867 8.37e-03 0.2092500 ns p = 0.20925 0.2640333 small
Tournament 1000 value Phenotypic Phylogenetic 50 50 791 1.57e-03 0.0392500
p = 0.03925 0.3164263 moderate
Tournament 10000 value Phenotypic Phylogenetic 50 50 195 0.00e+00 0.0000000 **** p < 1e-04 0.7272980 large
Tournament 100000 value Phenotypic Phylogenetic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large

2.7.3 Transfer entropy between types of diversity

While we’re calculating transfer entropy, we might as well also calculate it between phenotypic diversity and phylogenetic diversity, as these could potentially also be in a feedback loop.

2.7.3.1 Max pairwise distance and phenotypic richness

res <- data %>% group_by(directory, selection_name) %>%
summarise(
  phen_phylo_10 =      condinformation(discretize(phen_num_taxa), 
                                       discretize(lag(max_phenotype_pairwise_distance, 1)), 
                                       discretize(lag(phen_num_taxa, 1))),
  phen_phylo_100 =     condinformation(discretize(phen_num_taxa),
                                       discretize(lag(max_phenotype_pairwise_distance, 10)),
                                       discretize(lag(phen_num_taxa, 10))),
  pheno_phylo_1000 =   condinformation(discretize(phen_num_taxa),
                                       discretize(lag(max_phenotype_pairwise_distance, 100)),
                                       discretize(lag(phen_num_taxa, 100))),
  pheno_phylo_10000 =  condinformation(discretize(phen_num_taxa),
                                       discretize(lag(max_phenotype_pairwise_distance, 1000)),
                                       discretize(lag(phen_num_taxa, 1000))),
  pheno_phylo_100000 = condinformation(discretize(phen_num_taxa),
                                       discretize(lag(max_phenotype_pairwise_distance, 10000)),
                                       discretize(lag(phen_num_taxa, 10000))),
  
  phylo_pheno_10 =     condinformation(discretize(max_phenotype_pairwise_distance),
                                       discretize(lag(phen_num_taxa, 1)),
                                       discretize(lag(max_phenotype_pairwise_distance, 1))),
  phylo_pheno_100 =    condinformation(discretize(max_phenotype_pairwise_distance),
                                       discretize(lag(phen_num_taxa, 10)),
                                       discretize(lag(max_phenotype_pairwise_distance, 10))),
  phylo_pheno_1000 =   condinformation(discretize(max_phenotype_pairwise_distance),
                                       discretize(lag(phen_num_taxa, 100)),
                                       discretize(lag(max_phenotype_pairwise_distance, 100))),
  phylo_pheno_10000 =  condinformation(discretize(max_phenotype_pairwise_distance),
                                       discretize(lag(phen_num_taxa, 1000)),
                                       discretize(lag(max_phenotype_pairwise_distance, 1000))),
  phylo_pheno_100000 = condinformation(discretize(max_phenotype_pairwise_distance),
                                       discretize(lag(phen_num_taxa, 10000)),
                                       discretize(lag(max_phenotype_pairwise_distance, 10000)))
)

# Turn Transfer Entropy columns into rows
res <- res %>% pivot_longer(cols=contains("phylo"))
# Pull lag into a column
res$offset <- str_extract(res$name, "[:digit:]*$")
# Make column indicating direction of transfer entropy
res$Type <- case_when(str_detect(res$name, "phylo_pheno") ~ "\nPhenotypic\n\t->\nPhylogenetic\n", TRUE ~ "\nPhylogenetic\n\t->\nPhenotypic\n")

ggplot(
  res, 
  aes(
    x=as.factor(offset), 
    y=value, 
    color=Type
    )
  ) + 
  geom_boxplot() + 
  facet_wrap(~selection_name) + 
  scale_x_discrete("Lag",labels=c("10","","1000","","100000")) + 
  scale_y_continuous("Transfer Entropy") + 
  theme(legend.position = c(1, 0),
        legend.justification = c(1, 0)) + 
  scale_color_discrete("")

# Determine which conditions are significantly different from each other
transfer_entropy_stats(res)
selection_name offset .y. group1 group2 n1 n2 statistic p p.adj p.adj.signif label effsize magnitude
EcoEA 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 23 0.00e+00 0.0000000 **** p < 1e-04 0.8458717 large
EcoEA 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 35 0.00e+00 0.0000000 **** p < 1e-04 0.8375991 large
EcoEA 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 27 0.00e+00 0.0000000 **** p < 1e-04 0.8431142 large
EcoEA 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 38 0.00e+00 0.0000000 **** p < 1e-04 0.8355310 large
EcoEA 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 69 0.00e+00 0.0000000 **** p < 1e-04 0.8141601 large
Fitness Sharing 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 768 9.02e-04 0.0225500
p = 0.02255 0.3322821 moderate
Fitness Sharing 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 747 5.32e-04 0.0133000
p = 0.0133 0.3467591 moderate
Fitness Sharing 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 666 5.76e-05 0.0014400 ** p = 0.00144 0.4025991 moderate
Fitness Sharing 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 732 3.60e-04 0.0090000 ** p = 0.009 0.3570999 moderate
Fitness Sharing 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 745 5.05e-04 0.0126250
p = 0.012625 0.3481379 moderate
Lexicase 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Lexicase 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Lexicase 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Lexicase 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Lexicase 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1 0.00e+00 0.0000000 **** p < 1e-04 0.8610381 large
Random 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 15 0.00e+00 0.0000000 **** p < 1e-04 0.8513868 large
Random 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 2497 0.00e+00 0.0000000 **** p < 1e-04 0.8596593 large
Random 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 2479 0.00e+00 0.0000000 **** p < 1e-04 0.8472505 large
Random 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1849 3.69e-05 0.0009225 *** p = 0.0009225 0.4129398 moderate
Tournament 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 168 0.00e+00 0.0000000 **** p < 1e-04 0.7459113 large
Tournament 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 53 0.00e+00 0.0000000 **** p < 1e-04 0.8251902 large
Tournament 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 74 0.00e+00 0.0000000 **** p < 1e-04 0.8107132 large
Tournament 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 60 0.00e+00 0.0000000 **** p < 1e-04 0.8203646 large
Tournament 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 115 0.00e+00 0.0000000 **** p < 1e-04 0.7824486 large

2.7.3.2 Mean pairwise distance and phenotypic richness

res <- data %>% group_by(directory, selection_name) %>%
summarise(
  phen_phylo_10 =      condinformation(discretize(phen_num_taxa), 
                                       discretize(lag(mean_phenotype_pairwise_distance, 1)), 
                                       discretize(lag(phen_num_taxa, 1))),
  phen_phylo_100 =     condinformation(discretize(phen_num_taxa),
                                       discretize(lag(mean_phenotype_pairwise_distance, 10)),
                                       discretize(lag(phen_num_taxa, 10))),
  pheno_phylo_1000 =   condinformation(discretize(phen_num_taxa),
                                       discretize(lag(mean_phenotype_pairwise_distance, 100)),
                                       discretize(lag(phen_num_taxa, 100))),
  pheno_phylo_10000 =  condinformation(discretize(phen_num_taxa),
                                       discretize(lag(mean_phenotype_pairwise_distance, 1000)),
                                       discretize(lag(phen_num_taxa, 1000))),
  pheno_phylo_100000 = condinformation(discretize(phen_num_taxa),
                                       discretize(lag(mean_phenotype_pairwise_distance, 10000)),
                                       discretize(lag(phen_num_taxa, 10000))),
  
  phylo_pheno_10 =     condinformation(discretize(mean_phenotype_pairwise_distance),
                                       discretize(lag(phen_num_taxa, 1)),
                                       discretize(lag(mean_phenotype_pairwise_distance, 1))),
  phylo_pheno_100 =    condinformation(discretize(mean_phenotype_pairwise_distance),
                                       discretize(lag(phen_num_taxa, 10)),
                                       discretize(lag(mean_phenotype_pairwise_distance, 10))),
  phylo_pheno_1000 =   condinformation(discretize(mean_phenotype_pairwise_distance),
                                       discretize(lag(phen_num_taxa, 100)),
                                       discretize(lag(mean_phenotype_pairwise_distance, 100))),
  phylo_pheno_10000 =  condinformation(discretize(mean_phenotype_pairwise_distance),
                                       discretize(lag(phen_num_taxa, 1000)),
                                       discretize(lag(mean_phenotype_pairwise_distance, 1000))),
  phylo_pheno_100000 = condinformation(discretize(mean_phenotype_pairwise_distance),
                                       discretize(lag(phen_num_taxa, 10000)),
                                       discretize(lag(mean_phenotype_pairwise_distance, 10000)))
)

# Turn Transfer Entropy columns into rows
res <- res %>% pivot_longer(cols=contains("phylo"))
# Pull lag into a column
res$offset <- str_extract(res$name, "[:digit:]*$")
# Make column indicating direction of transfer entropy
res$Type <- case_when(str_detect(res$name, "phylo_pheno") ~ "\nPhenotypic\n\t->\nPhylogenetic\n", TRUE ~ "\nPhylogenetic\n\t->\nPhenotypic\n")

ggplot(
  res, 
  aes(
    x=as.factor(offset), 
    y=value, 
    color=Type
    )
  ) + 
  geom_boxplot() + 
  facet_wrap(~selection_name) + 
  scale_x_discrete("Lag",labels=c("10","","1000","","100000")) + 
  scale_y_continuous("Transfer Entropy") + 
  theme(legend.position = c(1, 0),
        legend.justification = c(1, 0)) + 
  scale_color_discrete("")

Here, on the other hand, there’s a pretty notable difference between mean and max pairwise distance. Looks like max pairwise distance is in general more predictive of phenotypic richness than mean pairwise distance is (for fitness sharing, mean richness even has a high transfer entropy to MPD than the other way around).

# Determine which conditions are significantly different from each other
transfer_entropy_stats(res)
selection_name offset .y. group1 group2 n1 n2 statistic p p.adj p.adj.signif label effsize magnitude
EcoEA 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 342 0.00e+00 0.0000000 **** p < 1e-04 0.6259588 large
EcoEA 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 488 2.00e-07 0.0000038 **** p < 1e-04 0.5253091 large
EcoEA 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 661 4.97e-05 0.0012425 ** p = 0.0012425 0.4060460 moderate
EcoEA 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 720 2.62e-04 0.0065500 ** p = 0.00655 0.3653725 moderate
EcoEA 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 735 3.90e-04 0.0097500 ** p = 0.00975 0.3550317 moderate
Fitness Sharing 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1602 1.54e-02 0.3850000 ns p = 0.385 0.2426625 small
Fitness Sharing 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1653 5.52e-03 0.1380000 ns p = 0.138 0.2778209 small
Fitness Sharing 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1369 4.14e-01 1.0000000 ns p = 1 0.0820365 small
Fitness Sharing 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1744 6.69e-04 0.0167250
p = 0.016725 0.3405547 moderate
Fitness Sharing 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1571 2.71e-02 0.6775000 ns p = 0.6775 0.2212916 small
Lexicase 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Lexicase 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Lexicase 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Lexicase 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 4 0.00e+00 0.0000000 **** p < 1e-04 0.8589700 large
Lexicase 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 409 0.00e+00 0.0000002 **** p < 1e-04 0.5797703 large
Random 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 2 0.00e+00 0.0000000 **** p < 1e-04 0.8603487 large
Random 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1771 3.33e-04 0.0083250 ** p = 0.008325 0.3591680 moderate
Random 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 2500 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 2473 0.00e+00 0.0000000 **** p < 1e-04 0.8431142 large
Random 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1749 5.89e-04 0.0147250
p = 0.014725 0.3440016 moderate
Tournament 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1298 7.43e-01 1.0000000 ns p = 1 0.0330903 small
Tournament 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1306 7.02e-01 1.0000000 ns p = 1 0.0386054 small
Tournament 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1301 7.28e-01 1.0000000 ns p = 1 0.0351585 small
Tournament 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1276 8.60e-01 1.0000000 ns p = 1 0.0179239 small
Tournament 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1280 8.39e-01 1.0000000 ns p = 1 0.0206815 small

2.7.3.3 Mean pairwise distance and shannon diversity

res <- data %>% group_by(directory, selection_name) %>%
summarise(
  phen_phylo_10 =      condinformation(discretize(phen_diversity), 
                                       discretize(lag(mean_phenotype_pairwise_distance, 1)), 
                                       discretize(lag(phen_diversity, 1))),
  phen_phylo_100 =     condinformation(discretize(phen_diversity),
                                       discretize(lag(mean_phenotype_pairwise_distance, 10)),
                                       discretize(lag(phen_diversity, 10))),
  pheno_phylo_1000 =   condinformation(discretize(phen_diversity),
                                       discretize(lag(mean_phenotype_pairwise_distance, 100)),
                                       discretize(lag(phen_diversity, 100))),
  pheno_phylo_10000 =  condinformation(discretize(phen_diversity),
                                       discretize(lag(mean_phenotype_pairwise_distance, 1000)),
                                       discretize(lag(phen_diversity, 1000))),
  pheno_phylo_100000 = condinformation(discretize(phen_diversity),
                                       discretize(lag(mean_phenotype_pairwise_distance, 10000)),
                                       discretize(lag(phen_diversity, 10000))),
  
  phylo_pheno_10 =     condinformation(discretize(mean_phenotype_pairwise_distance),
                                       discretize(lag(phen_diversity, 1)),
                                       discretize(lag(mean_phenotype_pairwise_distance, 1))),
  phylo_pheno_100 =    condinformation(discretize(mean_phenotype_pairwise_distance),
                                       discretize(lag(phen_diversity, 10)),
                                       discretize(lag(mean_phenotype_pairwise_distance, 10))),
  phylo_pheno_1000 =   condinformation(discretize(mean_phenotype_pairwise_distance),
                                       discretize(lag(phen_diversity, 100)),
                                       discretize(lag(mean_phenotype_pairwise_distance, 100))),
  phylo_pheno_10000 =  condinformation(discretize(mean_phenotype_pairwise_distance),
                                       discretize(lag(phen_diversity, 1000)),
                                       discretize(lag(mean_phenotype_pairwise_distance, 1000))),
  phylo_pheno_100000 = condinformation(discretize(mean_phenotype_pairwise_distance),
                                       discretize(lag(phen_diversity, 10000)),
                                       discretize(lag(mean_phenotype_pairwise_distance, 10000)))
)

# Turn Transfer Entropy columns into rows
res <- res %>% pivot_longer(cols=contains("phylo"))
# Pull lag into a column
res$offset <- str_extract(res$name, "[:digit:]*$")
# Make column indicating direction of transfer entropy
res$Type <- case_when(str_detect(res$name, "phylo_pheno") ~ "\nPhenotypic\n\t->\nPhylogenetic\n", TRUE ~ "\nPhylogenetic\n\t->\nPhenotypic\n")

ggplot(
  res, 
  aes(
    x=as.factor(offset), 
    y=value, 
    color=Type
    )
  ) + 
  geom_boxplot() + 
  facet_wrap(~selection_name) + 
  scale_x_discrete("Lag",labels=c("10","","1000","","100000")) + 
  scale_y_continuous("Transfer Entropy") + 
  theme(legend.position = c(1, 0),
        legend.justification = c(1, 0)) + 
  scale_color_discrete("")

# Determine which conditions are significantly different from each other
transfer_entropy_stats(res)
selection_name offset .y. group1 group2 n1 n2 statistic p p.adj p.adj.signif label effsize magnitude
EcoEA 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 101 0.00e+00 0.0000000 **** p < 1e-04 0.7920999 large
EcoEA 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 176 0.00e+00 0.0000000 **** p < 1e-04 0.7403963 large
EcoEA 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 91 0.00e+00 0.0000000 **** p < 1e-04 0.7989937 large
EcoEA 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 119 0.00e+00 0.0000000 **** p < 1e-04 0.7796910 large
EcoEA 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 473 1.00e-07 0.0000022 **** p < 1e-04 0.5356498 large
Fitness Sharing 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 884 1.17e-02 0.2925000 ns p = 0.2925 0.2523138 small
Fitness Sharing 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 905 1.76e-02 0.4400000 ns p = 0.44 0.2378368 small
Fitness Sharing 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 836 4.36e-03 0.1090000 ns p = 0.109 0.2854041 small
Fitness Sharing 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 994 7.82e-02 1.0000000 ns p = 1 0.1764818 small
Fitness Sharing 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1247 9.86e-01 1.0000000 ns p = 1 0.0020681 small
Lexicase 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 6 0.00e+00 0.0000000 **** p < 1e-04 0.8575912 large
Lexicase 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 9 0.00e+00 0.0000000 **** p < 1e-04 0.8555230 large
Lexicase 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 44 0.00e+00 0.0000000 **** p < 1e-04 0.8313947 large
Lexicase 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 24 0.00e+00 0.0000000 **** p < 1e-04 0.8451823 large
Lexicase 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 195 0.00e+00 0.0000000 **** p < 1e-04 0.7272980 large
Random 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 949 3.83e-02 0.9575000 ns p = 0.9575 0.2075040 small
Random 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1558 3.40e-02 0.8500000 ns p = 0.85 0.2123297 small
Random 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1304 7.12e-01 1.0000000 ns p = 1 0.0372266 small
Tournament 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1232 9.04e-01 1.0000000 ns p = 1 0.0124089 small
Tournament 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1261 9.42e-01 1.0000000 ns p = 1 0.0075832 small
Tournament 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1252 9.92e-01 1.0000000 ns p = 1 0.0013788 small
Tournament 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1217 8.23e-01 1.0000000 ns p = 1 0.0227496 small
Tournament 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1240 9.48e-01 1.0000000 ns p = 1 0.0068938 small

2.7.3.4 Mean evolutionary distinctiveness and phenotypic richness

res <- data %>% group_by(directory, selection_name) %>%
summarise(
  phen_phylo_10 =      condinformation(
                          discretize(phen_num_taxa), 
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 1)), 
                          discretize(lag(phen_num_taxa, 1))),
  phen_phylo_100 =     condinformation(
                          discretize(phen_num_taxa),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 10)),
                          discretize(lag(phen_num_taxa, 10))),
  pheno_phylo_1000 =   condinformation(
                          discretize(phen_num_taxa),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 100)),
                          discretize(lag(phen_num_taxa, 100))),
  pheno_phylo_10000 =  condinformation(
                          discretize(phen_num_taxa),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 1000)),
                          discretize(lag(phen_num_taxa, 1000))),
  pheno_phylo_100000 = condinformation(
                          discretize(phen_num_taxa),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 10000)),
                          discretize(lag(phen_num_taxa, 10000))),
  
  phylo_pheno_10 =     condinformation(
                          discretize(mean_phenotype_evolutionary_distinctiveness),
                          discretize(lag(phen_num_taxa, 1)),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 1))),
  phylo_pheno_100 =    condinformation(
                          discretize(mean_phenotype_evolutionary_distinctiveness),
                          discretize(lag(phen_num_taxa, 10)),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 10))),
  phylo_pheno_1000 =   condinformation(
                          discretize(mean_phenotype_evolutionary_distinctiveness),
                          discretize(lag(phen_num_taxa, 100)),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 100))),
  phylo_pheno_10000 =  condinformation(
                          discretize(mean_phenotype_evolutionary_distinctiveness),
                          discretize(lag(phen_num_taxa, 1000)),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 1000))),
  phylo_pheno_100000 = condinformation(
                          discretize(mean_phenotype_evolutionary_distinctiveness),
                          discretize(lag(phen_num_taxa, 10000)),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 10000)))
)

# Turn Transfer Entropy columns into rows
res <- res %>% pivot_longer(cols=contains("phylo"))
# Pull lag into a column
res$offset <- str_extract(res$name, "[:digit:]*$")
# Make column indicating direction of transfer entropy
res$Type <- case_when(str_detect(res$name, "phylo_pheno") ~ "\nPhenotypic\n\t->\nPhylogenetic\n", TRUE ~ "\nPhylogenetic\n\t->\nPhenotypic\n")

ggplot(
  res, 
  aes(
    x=as.factor(offset), 
    y=value, 
    color=Type
    )
  ) + 
  geom_boxplot() + 
  facet_wrap(~selection_name) + 
  scale_x_discrete("Lag",labels=c("10","","1000","","100000")) + 
  scale_y_continuous("Transfer Entropy") + 
  theme(legend.position = c(1, 0),
        legend.justification = c(1, 0)) + 
  scale_color_discrete("")

# Determine which conditions are significantly different from each other
transfer_entropy_stats(res)
selection_name offset .y. group1 group2 n1 n2 statistic p p.adj p.adj.signif label effsize magnitude
EcoEA 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 25 0.00e+00 0.000000 **** p < 1e-04 0.8444929 large
EcoEA 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 96 0.00e+00 0.000000 **** p < 1e-04 0.7955468 large
EcoEA 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 175 0.00e+00 0.000000 **** p < 1e-04 0.7410856 large
EcoEA 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 216 0.00e+00 0.000000 **** p < 1e-04 0.7128210 large
EcoEA 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 471 1.00e-07 0.000002 **** p < 1e-04 0.5370286 large
Fitness Sharing 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 817 2.87e-03 0.071750 ns p = 0.07175 0.2985024 small
Fitness Sharing 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 946 3.64e-02 0.910000 ns p = 0.91 0.2095721 small
Fitness Sharing 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1303 7.17e-01 1.000000 ns p = 1 0.0365372 small
Fitness Sharing 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1795 1.74e-04 0.004350 ** p = 0.00435 0.3757132 moderate
Fitness Sharing 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1693 2.28e-03 0.057000 ns p = 0.057 0.3053962 moderate
Lexicase 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.000000 **** p < 1e-04 0.8617275 large
Lexicase 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.000000 **** p < 1e-04 0.8617275 large
Lexicase 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.000000 **** p < 1e-04 0.8617275 large
Lexicase 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.000000 **** p < 1e-04 0.8617275 large
Lexicase 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 154 0.00e+00 0.000000 **** p < 1e-04 0.7555627 large
Random 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1159 5.33e-01 1.000000 ns p = 1 0.0627338 small
Random 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 2482 0.00e+00 0.000000 **** p < 1e-04 0.8493186 large
Random 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 2500 0.00e+00 0.000000 **** p < 1e-04 0.8617275 large
Random 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 2474 0.00e+00 0.000000 **** p < 1e-04 0.8438036 large
Random 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1707 1.65e-03 0.041250
p = 0.04125 0.3150476 moderate
Tournament 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 356 0.00e+00 0.000000 **** p < 1e-04 0.6163075 large
Tournament 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 454 0.00e+00 0.000001 **** p < 1e-04 0.5487481 large
Tournament 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1154 5.10e-01 1.000000 ns p = 1 0.0661807 small
Tournament 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1843 4.42e-05 0.001105 ** p = 0.001105 0.4088035 moderate
Tournament 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1736 8.17e-04 0.020425
p = 0.020425 0.3350396 moderate

2.7.3.5 Mean evolutionary distinctiveness and shannon diversity

res <- data %>% group_by(directory, selection_name) %>%
summarise(
  phen_phylo_10 =      condinformation(
                          discretize(phen_diversity), 
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 1)), 
                          discretize(lag(phen_diversity, 1))),
  phen_phylo_100 =     condinformation(
                          discretize(phen_diversity),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 10)),
                          discretize(lag(phen_diversity, 10))),
  pheno_phylo_1000 =   condinformation(
                          discretize(phen_diversity),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 100)),
                          discretize(lag(phen_diversity, 100))),
  pheno_phylo_10000 =  condinformation(
                          discretize(phen_diversity),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 1000)),
                          discretize(lag(phen_diversity, 1000))),
  pheno_phylo_100000 = condinformation(
                          discretize(phen_diversity),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 10000)),
                          discretize(lag(phen_diversity, 10000))),
  
  phylo_pheno_10 =     condinformation(
                          discretize(mean_phenotype_evolutionary_distinctiveness),
                          discretize(lag(phen_diversity, 1)),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 1))),
  phylo_pheno_100 =    condinformation(
                          discretize(mean_phenotype_evolutionary_distinctiveness),
                          discretize(lag(phen_diversity, 10)),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 10))),
  phylo_pheno_1000 =   condinformation(
                          discretize(mean_phenotype_evolutionary_distinctiveness),
                          discretize(lag(phen_diversity, 100)),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 100))),
  phylo_pheno_10000 =  condinformation(
                          discretize(mean_phenotype_evolutionary_distinctiveness),
                          discretize(lag(phen_diversity, 1000)),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 1000))),
  phylo_pheno_100000 = condinformation(
                          discretize(mean_phenotype_evolutionary_distinctiveness),
                          discretize(lag(phen_diversity, 10000)),
                          discretize(lag(mean_phenotype_evolutionary_distinctiveness, 10000)))
)

# Turn Transfer Entropy columns into rows
res <- res %>% pivot_longer(cols=contains("phylo"))
# Pull lag into a column
res$offset <- str_extract(res$name, "[:digit:]*$")
# Make column indicating direction of transfer entropy
res$Type <- case_when(str_detect(res$name, "phylo_pheno") ~ "\nPhenotypic\n\t->\nPhylogenetic\n", TRUE ~ "\nPhylogenetic\n\t->\nPhenotypic\n")

ggplot(
  res, 
  aes(
    x=as.factor(offset), 
    y=value, 
    color=Type
    )
  ) + 
  geom_boxplot() + 
  facet_wrap(~selection_name) + 
  scale_x_discrete("Lag",labels=c("10","","1000","","100000")) + 
  scale_y_continuous("Transfer Entropy") + 
  theme(legend.position = c(1, 0),
        legend.justification = c(1, 0)) + 
  scale_color_discrete("")

# Determine which conditions are significantly different from each other
transfer_entropy_stats(res)
selection_name offset .y. group1 group2 n1 n2 statistic p p.adj p.adj.signif label effsize magnitude
EcoEA 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
EcoEA 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
EcoEA 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 5 0.00e+00 0.0000000 **** p < 1e-04 0.8582806 large
EcoEA 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 2 0.00e+00 0.0000000 **** p < 1e-04 0.8603487 large
EcoEA 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 289 0.00e+00 0.0000000 **** p < 1e-04 0.6624961 large
Fitness Sharing 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 768 9.02e-04 0.0225500
p = 0.02255 0.3322821 moderate
Fitness Sharing 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 762 7.77e-04 0.0194250
p = 0.019425 0.3364184 moderate
Fitness Sharing 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 809 2.39e-03 0.0597500 ns p = 0.05975 0.3040175 moderate
Fitness Sharing 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 988 7.14e-02 1.0000000 ns p = 1 0.1806181 small
Fitness Sharing 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1312 6.72e-01 1.0000000 ns p = 1 0.0427417 small
Lexicase 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Lexicase 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Lexicase 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Lexicase 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Lexicase 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 23 0.00e+00 0.0000000 **** p < 1e-04 0.8458717 large
Random 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 0 0.00e+00 0.0000000 **** p < 1e-04 0.8617275 large
Random 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1208 7.75e-01 1.0000000 ns p = 1 0.0289540 small
Random 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1244 9.70e-01 1.0000000 ns p = 1 0.0041363 small
Random 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1257 9.64e-01 1.0000000 ns p = 1 0.0048257 small
Tournament 10 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 394 0.00e+00 0.0000001 **** p < 1e-04 0.5901110 large
Tournament 100 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 482 1.00e-07 0.0000030 **** p < 1e-04 0.5294454 large
Tournament 1000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1107 3.26e-01 1.0000000 ns p = 1 0.0985816 small
Tournament 10000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1739 7.58e-04 0.0189500
p = 0.01895 0.3371078 moderate
Tournament 100000 value Phenotypic -> Phylogenetic Phylogenetic -> Phenotypic 50 50 1675 3.43e-03 0.0857500 ns p = 0.08575 0.2929873 small

References

Allen, Micah, Davide Poggiali, Kirstie Whitaker, Tom Rhys Marshall, Jordy van Langen, and Rogier A. Kievit. 2021. “Raincloud Plots: A Multi-Platform Tool for Robust Data Visualization.” Wellcome Open Research 4 (January): 63. https://doi.org/10/ghvbc6.

Hernandez, Jose Guadalupe, Alexander Lalejini, and Charles Ofria. 2021. “An Exploration of Exploration: Measuring the Ability of Lexicase Selection to Find Obscure Pathways to Optimality.” arXiv:2107.09760 [Cs], July. http://arxiv.org/abs/2107.09760.