Skip to content
This repository has been archived by the owner on Jun 23, 2023. It is now read-only.

Commit

Permalink
Merge pull request #175 from EcoJulia/doc/examples
Browse files Browse the repository at this point in the history
Additional examples
  • Loading branch information
tpoisot authored Nov 27, 2020
2 parents afc2a97 + 7336853 commit fb877e8
Show file tree
Hide file tree
Showing 13 changed files with 415 additions and 70 deletions.
20 changes: 2 additions & 18 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
version:
- '1.3'
- '1.4'
- 'nightly'
- '1.5'
os:
- ubuntu-latest
- macOS-latest
Expand All @@ -38,20 +38,4 @@ jobs:
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
token: ${{ secrets.CODECOV }}
docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@v1
with:
version: '1.4'
- run: |
julia --project=docs -e '
using Pkg
Pkg.develop(PackageSpec(path=pwd()))
Pkg.instantiate()'
- run: julia --project=docs docs/make.jl
env:
GITHUB_TOKEN: ${{ secrets.TOKEN }}
token: ${{ secrets.CODECOV }}
24 changes: 24 additions & 0 deletions .github/workflows/Documentation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Documentation

on:
release:
push:
branches:
- master
tags: '*'
pull_request:

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@latest
with:
version: '1.5'
- name: Install dependencies
run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()'
- name: Build and deploy
env:
GITHUB_TOKEN: ${{ secrets.TOKEN }} # For authentication with GitHub Actions token
run: julia --project=docs/ docs/make.jl
2 changes: 1 addition & 1 deletion .github/workflows/TagBot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ jobs:
steps:
- uses: JuliaRegistries/TagBot@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ secrets.TOKEN }}
1 change: 1 addition & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[deps]
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
EcologicalNetworksPlots = "9f7a259d-73a7-556d-a7a2-3eb122d3865b"
Mangal = "b8b640a6-63d9-51e6-b784-5033db27bef2"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Expand Down
75 changes: 40 additions & 35 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,45 @@ using EcologicalNetworks
using Random

makedocs(
sitename = "EcologicalNetworks",
authors = "Timothée Poisot",
modules = [EcologicalNetworks],
pages = [
"Index" => "index.md",
"Interface" => [
"Types" => "interface/types.md",
"Conversions" => "interface/conversions.md",
"Core functions" => "interface/highlevel.md"
],
"Network measures" => [
"Links" => "properties/links.md",
"Modularity" => "properties/modularity.md",
"Nestedness" => "properties/nestedness.md",
"Motifs" => "properties/motifs.md",
"Centrality and paths" => "properties/paths.md",
"Overlap and similarity" => "properties/overlap.md",
"Beta-diversity" => "properties/betadiversity.md",
"Resilience" => "properties/resilience.md",
"Information theory" => "properties/information.md"
],
"Random networks" => [
"Null models" => "random/null.md",
"Structural models" => "random/structure.md"
],
"Examples" => [
"Integration with Mangal" => "examples/integration_mangal.md"
]
]
)
sitename = "EcologicalNetworks",
authors = "Timothée Poisot",
modules = [EcologicalNetworks],
pages = [
"Index" => "index.md",
"Interface" => [
"Types" => "interface/types.md",
"Conversions" => "interface/conversions.md",
"Core functions" => "interface/highlevel.md"
],
"Examples" => [
"Modularity" => "examples/modularity.md",
"Integration with Mangal" => "examples/integration_mangal.md",
"Network beta-diversity" => "examples/beta-diversity.md",
"Extinctions" => "examples/extinctions.md"
],
"Basic measures" => [
"Links" => "properties/links.md",
"Modularity" => "properties/modularity.md",
"Nestedness" => "properties/nestedness.md",
"Motifs" => "properties/motifs.md",
"Centrality and paths" => "properties/paths.md",
"Overlap and similarity" => "properties/overlap.md"
],
"Advanced information" => [
"Beta-diversity" => "properties/betadiversity.md",
"Resilience" => "properties/resilience.md",
"Information theory" => "properties/information.md"
],
"Generating networks" => [
"Null models" => "random/null.md",
"Structural models" => "random/structure.md"
]
]
)

deploydocs(
deps = Deps.pip("pygments", "python-markdown-math"),
repo = "github.com/EcoJulia/EcologicalNetworks.jl.git",
devbranch = "master",
push_preview = true
)
deps = Deps.pip("pygments", "python-markdown-math"),
repo = "github.com/EcoJulia/EcologicalNetworks.jl.git",
devbranch = "master",
push_preview = true
)
Binary file modified docs/src/assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 72 additions & 0 deletions docs/src/examples/beta-diversity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@

# Network beta-diversity

In this section, we will measure the dissimilarity between bipartite
host-parasite networks.

```@example betadiv
using EcologicalNetworks
using Plots
using Plots.PlotMeasures
```

We use networks that span the entirety of Eurasia. Because these networks are
originally quantitative, we will remove the information on interaction strength
using `convert`. Note that we convert to an union type (`BinaryNetwork`) -- the
`convert` function will select the appropriate network type to return based on
the partiteness. The core operations on sets (`union`, `diff`, and `intersect`)
are implemented for the `BinaryNetwork` type. As such, generating the "metaweb"
(*i.e.* the list of all species and all interactions in the complete dataset)
is:

```@example betadiv
all_hp_data = filter(x -> occursin("Hadfield", x.Reference), web_of_life());
ids = getfield.(all_hp_data, :ID);
networks = convert.(BinaryNetwork, web_of_life.(ids));
metaweb = reduce(union, networks)
```

From this metaweb, we can measure $\beta_{OS}'$, *i.e.* the dissimilarity of
every network to the expectation in the metaweb. Measuring the distance between
two networks is done in two steps. Dissimilarity is first partitioned into three
components (common elements, and elements unique to both samples), then the
value is measured based on the cardinality of these components. The functions to
generate the partitions are `βos` (dissimilarity of interactions between shared
species), `βs` (dissimilarity of species composition), and `βwn` (whole network
dissimilarity). The output of these functions is passed to one of the functions
to measure the actual $β$-diversity.

```@example betadiv
βcomponents = [βos(metaweb, n) for n in networks];
βosprime = KGL02.(βcomponents);
```

Finally, we measure the pairwise distance between all networks (because we use a
symmetric measure, we only need $n\times(n-1)$ distances):

```@example betadiv
S, OS, WN = Float64[], Float64[], Float64[]
for i in 1:(length(networks)-1)
for j in (i+1):length(networks)
push!(S, KGL02(βs(networks[i], networks[j])))
push!(OS, KGL02(βos(networks[i], networks[j])))
push!(WN, KGL02(βwn(networks[i], networks[j])))
end
end
```

We can now visualize these data:

```@example betadiv
p1 = histogram(βosprime, frame=:origin, bins=20, c=:white, leg=false, grid=false, margin=10mm)
xaxis!(p1, "Difference to metaweb", (0,1))
yaxis!(p1, (0,10))
p2 = plot([0,1],[0,1], c=:grey, ls=:dash, frame=:origin, grid=false, lab="", legend=:bottomleft, margin=10mm)
scatter!(p2, S, OS, mc=:black, lab="shared sp.", msw=0.0)
scatter!(p2, S, WN, mc=:lightgrey, lab="all sp.", msw=0.0, m=:diamond)
xaxis!(p2, "Species dissimilarity", (0,1))
yaxis!(p2, "Network dissimilarity", (0,1))
plot(p1,p2, size=(700,300))
```
78 changes: 78 additions & 0 deletions docs/src/examples/extinctions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
In this illustration, we will simulate extinctions of hosts, to show how the
package can be extended by using the core functions described in the "Interface"
section. Simply put, the goal of this example is to write a function to randomly
remove one host species, remove all parasite species that end up not connected
to a host, and measuring the effect of these extinctions on the remaining
network. Rather than measuring the network structure in the function, we will
return an array of networks to be manipulated later:

```@example ext
using EcologicalNetworks
using Plots
using Plots.PlotMeasures
```

```@example ext
function extinctions(N::T) where {T <: AbstractBipartiteNetwork}
# We start by making a copy of the network to extinguish
Y = [copy(N)]
# While there is at least one species remaining...
while richness(last(Y)) > 1
# We remove one species randomly
remain = sample(species(last(Y); dims=2), richness(last(Y); dims=2)-1, replace=false)
# Remaining species
R = last(Y)[:,remain]
simplify!(R)
# Then add the simplified network (without the extinct species) to our collection
push!(Y, copy(R))
end
return Y
end
```

One classical analysis is to remove host species, and count the richness of
parasite species, to measure their robustness to host extinctions -- this is
usually done with multiple scenarios for order of extinction, but we will focus
on the random order here. Even though `EcologicalNetworks` has a built-in
function for richness, we can write a small wrapper around it:

```@example ext
function parasite_richness(N::T) where {T<:BinaryNetwork}
return richness(N; dims=1)
end
```

Writing multiple functions that take a single argument allows to chain them in a
very expressive way: for example, measuring the richness on all timesteps in a
simulation is `N |> extinctions .|> parasite_richness`, or alternatively,
`parasite_richness.(extinctions(N))`. In @fig:extinctions, we illustrate the
output of this analysis on 100 simulations (average and standard deviation) for
one of the networks.

```@example ext
N = web_of_life("A_HP_050")
X = Float64[]
Y = Float64[]
for i in 1:200
timeseries = extinctions(N)
path_l = parasite_richness.(timeseries)./richness(N; dims=1)
prop_r = 1.0.-richness.(timeseries; dims=2)./richness(N; dims=2)
append!(X, prop_r)
append!(Y, path_l)
end
x = sort(unique(X))
y = zeros(Float64, length(x))
sy = zeros(Float64, length(x))
for (i, tx) in enumerate(x)
y[i] = mean(Y[X.==tx])
sy[i] = std(Y[X.==tx])
end
pl = plot(x, y, ribbon=sy, c=:black, fill=(:lightgrey), lw=2, ls=:dash, leg=false, margin = 10mm, grid=false, frame=:origin, xlim=(0,1), ylim=(0,1))
xaxis!(pl, "Proportion of hosts removed")
```
74 changes: 74 additions & 0 deletions docs/src/examples/imputation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
## Interaction imputation

In the final example, we will apply the linear filtering method of @StocPois17
to suggest which negative interactions may have been missed in a network.
Starting from a binary network, this approach generates a quantitative network,
in which the weight of each interaction is the likelihood that it exists -- for
interactions absent from the original network, this suggests that they may have
been missed during sampling. This makes this approach interesting to guide
empirical efforts during the notoriously difficult task of sampling ecological
networks [@Jord16; @Jord16a].

In the approach of @StocPois17, the filtered interaction matrix (*i.e.* the
network of weights) is given by

\begin{equation}
F_{ij} = \alpha_1Y_{ij} + \alpha_2\sum_k\frac{Y_{kj}}{n} + \alpha_3\sum_l\frac{Y_{il}}{m} + \alpha_4\frac{\sum Y}{n\times m} \,,
\end{equation}

where $\alpha$ is a vector of weights summing to 1, and $(n,m)$ is the size of
the network. Note that the sums along rows and columns are actually the in and
out degree of species. This is implemented in `EcologicalNetworks` as the
`linearfilter` function. As in @StocPois17, we set all values in $\alpha$ to
$1/4$. We can now use this function to get the top interaction that, although
absent from the sampled network, is a strong candidate to exist based on the
linear filtering output:

```julia
N = networks[50]
F = linearfilter(N)
```

We would like to separate the weights in 3: observed interactions, interactions
that are not observed in this network but are observed in the metaweb, and
interactions that are never observed. `EcologicalNetworks` has the
`has_interaction` function to test this, but because `BinaryNetwork` are using
Boolean values, we can look at the network directly:

```julia
scores_present = sort(
filter(int -> N[int.from, int.to], interactions(F)),
by = int -> int.probability,
rev = true);

scores_metaweb = sort(
filter(int -> (!N[int.from,int.to])&(metaweb[int.from, int.to]), interactions(F)),
by = int -> int.probability,
rev = true);

scores_absent = sort(
filter(int -> !metaweb[int.from,int.to], interactions(F)),
by = int -> int.probability,
rev = true);
```

The results of this analysis are presented in @fig:imputation: the weights
$F_{ij}$ of interactions that are present locally ($Y_{ij}=\text{true}$) are
*always* larger that the weight of interactions that are absent; furthermore,
the weight of interactions that are absent locally are equal to the weight of
interactions that are also absent globally, strongly suggesting that this
network has been correctly sampled.

```julia; echo=false
nx, ny = range(0.0, stop=1.0, length=length(scores_present)), [x.probability for x in scores_present]
pl = plot(nx, ny, grid=false, frame=:origin, lw=2, lab="Present locally", c=:black)
nx, ny = range(0.0, stop=1.0, length=length(scores_metaweb)), [x.probability for x in scores_metaweb]
plot!(pl, nx, ny, c=:black, lab="Present globally")
nx, ny = range(0.0, stop=1.0, length=length(scores_absent)), [x.probability for x in scores_absent]
plot!(pl, nx, ny, c=:darkgrey, ls=:dash, lab="Absent")
xaxis!(pl, "Relative rank", (0,1))
yaxis!(pl, "Interaction weight", (0,1))
savefig("figures/imputation.pdf")
```

![Relative weights (higher weights indicates a larger chance that the interaction has been missed when sampling) in one of the host-parasite networks according to the linear filter model of @StocPois17.](figures/imputation.pdf){#fig:imputation}
Loading

2 comments on commit fb877e8

@tpoisot
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/25397

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.3.1 -m "<description of version>" fb877e85067eef3ed076db47384175480534e62f
git push origin v0.3.1

Please sign in to comment.