mirror of
https://github.com/MillironX/beefblup.git
synced 2025-01-02 20:22:09 -05:00
Run Julia template engine for CI purposes
This commit is contained in:
parent
95fd34ce3a
commit
0a537f9928
8 changed files with 60 additions and 352 deletions
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
BSD 3-Clause License
|
BSD 3-Clause License
|
||||||
|
|
||||||
Copyright (c) 2020, Thomas A. Christensen II
|
Copyright (c) 2021, Thomas A. Christensen II <25492070+MillironX@users.noreply.github.com> and contributors
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
|
21
Project.toml
21
Project.toml
|
@ -1,12 +1,13 @@
|
||||||
name = "BeefBLUP"
|
name = "BeefBLUP"
|
||||||
uuid = "03993faf-e476-444a-86c9-f31e8122fa24"
|
uuid = "4c4dd571-0773-455e-9a95-72727008e3f4"
|
||||||
authors = ["Thomas A. Christensen II <25492070+MillironX@users.noreply.github.com>"]
|
authors = ["Thomas A. Christensen II <25492070+MillironX@users.noreply.github.com> and contributors"]
|
||||||
version = "0.3.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
[deps]
|
[compat]
|
||||||
ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63"
|
julia = "1.3"
|
||||||
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
|
|
||||||
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
|
[extras]
|
||||||
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
|
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||||
Gtk = "4c0ca9eb-093a-5379-98c5-f87ac0bbbf44"
|
|
||||||
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
[targets]
|
||||||
|
test = ["Test"]
|
||||||
|
|
83
README.md
83
README.md
|
@ -1,82 +1,3 @@
|
||||||
# [:cow:]: beefblup
|
# BeefBLUP
|
||||||
|
|
||||||
[![GitHub license](https://img.shields.io/github/license/MillironX/beefblup)](https://github.com/MillironX/beefblup/blob/master/LICENSE.md)
|
[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://MillironX.github.io/BeefBLUP.jl/stable) [![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://MillironX.github.io/BeefBLUP.jl/dev) [![Build Status](https://travis-ci.com/MillironX/BeefBLUP.jl.svg?branch=master)](https://travis-ci.com/MillironX/BeefBLUP.jl) [![Coverage](https://codecov.io/gh/MillironX/BeefBLUP.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/MillironX/BeefBLUP.jl) [![Code Style: Blue](https://img.shields.io/badge/code%20style-blue-4495d1.svg)](https://github.com/invenia/BlueStyle)
|
||||||
[![Join the chat at https://gitter.im/beefblup/community](https://badges.gitter.im/beefblup/community.svg)](https://gitter.im/beefblup/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
||||||
[![Github all releases](https://img.shields.io/github/downloads/MillironX/beefblup/total.svg)](https://GitHub.com/MillironX/beefblup/releases)
|
|
||||||
|
|
||||||
beefblup is a program for ranchers to calculate expected breeding
|
|
||||||
values (EBVs) for their own beef cattle. It is intended to be usable by anyone
|
|
||||||
without requiring any prior knowledge of computer programming or linear algebra.
|
|
||||||
Why? It's part of my effort to
|
|
||||||
**\#KeepEPDsReal**
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
1. [Download and install Julia](https://julialang.org/downloads/platform/)
|
|
||||||
2. Download the [beefblup ZIP
|
|
||||||
file](https://github.com/MillironX/beefblup/archive/refs/tags/v0.2.zip) and unzip it someplace memorable
|
|
||||||
3. In your file explorer, copy the address of the "beefblup" folder
|
|
||||||
4. Launch Julia
|
|
||||||
5. Type `cd("<the address copied in step 5")` and press **Enter** (For example,
|
|
||||||
`cd("C:\Users\MillironX\Documents\beefblup")`)
|
|
||||||
6. Type the `]` key
|
|
||||||
7. Type `activate .` and press **Enter**
|
|
||||||
8. Type `instantiate` and press **Enter**
|
|
||||||
9. Installation is done: you can close the Julia window
|
|
||||||
|
|
||||||
## How to Use
|
|
||||||
|
|
||||||
1. Make a copy of the "sample.csv" spreadsheet and replace the data with your own
|
|
||||||
1. The trait you wish to calculate EBVs for always goes in the rightmost column
|
|
||||||
2. If you wish to add more contemporary group traits to your analysis, include them before the rightmost column
|
|
||||||
2. Save and close
|
|
||||||
3. In your file explorer, copy the address of the "beefblup" folder
|
|
||||||
4. Launch Julia
|
|
||||||
5. Type `cd("<the address copied in step 5")` and press **Enter** (For example,
|
|
||||||
`cd("C:\Users\MillironX\Documents\beefblup")`)
|
|
||||||
6. Type the `]` key
|
|
||||||
7. Type `activate .` and press **Enter**
|
|
||||||
8. Press **Backspace**
|
|
||||||
9. Type `include("src/beefblup.jl")` and press **Enter**
|
|
||||||
10. Select the spreadsheet you created in steps 1-4
|
|
||||||
11. Follow the on-screen prompts
|
|
||||||
12. **#KeepEPDsReal!**
|
|
||||||
|
|
||||||
## For Programmers
|
|
||||||
|
|
||||||
> **Also Note:** beefblup was written on, and has only been tested with Julia
|
|
||||||
> v1.2.0 and higher. While this shouldn't affect most everyday users, it might
|
|
||||||
> affect you if you are stuck on the current LTS version of Julia (v1.0.5).
|
|
||||||
|
|
||||||
### Development Roadmap
|
|
||||||
|
|
||||||
| Version | Feature | Status |
|
|
||||||
| ------- | ----------------------------------------------------------------------------------- | ------------------ |
|
|
||||||
| v0.1 | Julia port of original MATLAB script | :heavy_check_mark: |
|
|
||||||
| v0.2 | Spreadsheet format redesign | :heavy_check_mark: |
|
|
||||||
| v0.3 | API rewrite (change to function calls and package format instead of script running) | |
|
|
||||||
| v0.4 | Add GUI for all options | |
|
|
||||||
| v0.5 | Automatically calculated Age-Of-Dam, Year, and Season fixed-effects | |
|
|
||||||
| v0.6 | Repeated measurement BLUP (aka dairyblup) | |
|
|
||||||
| v0.7 | Multiple trait BLUP | |
|
|
||||||
| v0.8 | Maternal effects BLUP | |
|
|
||||||
| v0.9 | Genomic BLUP | |
|
|
||||||
| v0.10 | beefblup binaries | |
|
|
||||||
| v1.0 | [Finally, RELEASE!!!](https://youtu.be/1CBjxGdgC1w?t=282) | |
|
|
||||||
|
|
||||||
### Bug Reports
|
|
||||||
|
|
||||||
For every bug report, please include at least the following:
|
|
||||||
|
|
||||||
- Platform (Windows, Mac, Fedora, etc)
|
|
||||||
- Julia version
|
|
||||||
- beefblup version
|
|
||||||
- How you are running Julia (From PowerShell, via the REPL, etc.)
|
|
||||||
- A beefblup spreadsheet that can be used to recreate the issue
|
|
||||||
- Description of the problem
|
|
||||||
- Expected behavior
|
|
||||||
- A screenshot and/or REPL printout
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Distributed under the 3-Clause BSD License
|
|
||||||
|
|
3
docs/Project.toml
Normal file
3
docs/Project.toml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[deps]
|
||||||
|
BeefBLUP = "4c4dd571-0773-455e-9a95-72727008e3f4"
|
||||||
|
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
|
24
docs/make.jl
Normal file
24
docs/make.jl
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
using BeefBLUP
|
||||||
|
using Documenter
|
||||||
|
|
||||||
|
DocMeta.setdocmeta!(BeefBLUP, :DocTestSetup, :(using BeefBLUP); recursive=true)
|
||||||
|
|
||||||
|
makedocs(;
|
||||||
|
modules=[BeefBLUP],
|
||||||
|
authors="Thomas A. Christensen II <25492070+MillironX@users.noreply.github.com> and contributors",
|
||||||
|
repo="https://github.com/MillironX/BeefBLUP.jl/blob/{commit}{path}#{line}",
|
||||||
|
sitename="BeefBLUP.jl",
|
||||||
|
format=Documenter.HTML(;
|
||||||
|
prettyurls=get(ENV, "CI", "false") == "true",
|
||||||
|
canonical="https://MillironX.github.io/BeefBLUP.jl",
|
||||||
|
assets=String[],
|
||||||
|
),
|
||||||
|
pages=[
|
||||||
|
"Home" => "index.md",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
deploydocs(;
|
||||||
|
repo="github.com/MillironX/BeefBLUP.jl",
|
||||||
|
devbranch="develop",
|
||||||
|
)
|
14
docs/src/index.md
Normal file
14
docs/src/index.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
```@meta
|
||||||
|
CurrentModule = BeefBLUP
|
||||||
|
```
|
||||||
|
|
||||||
|
# BeefBLUP
|
||||||
|
|
||||||
|
Documentation for [BeefBLUP](https://github.com/MillironX/BeefBLUP.jl).
|
||||||
|
|
||||||
|
```@index
|
||||||
|
```
|
||||||
|
|
||||||
|
```@autodocs
|
||||||
|
Modules = [BeefBLUP]
|
||||||
|
```
|
261
src/BeefBLUP.jl
Executable file → Normal file
261
src/BeefBLUP.jl
Executable file → Normal file
|
@ -1,264 +1,5 @@
|
||||||
# beefblup
|
|
||||||
# Julia package for performing single-variate BLUP to find beef cattle
|
|
||||||
# breeding values
|
|
||||||
# (C) 2021 Thomas A. Christensen II
|
|
||||||
# Licensed under BSD-3-Clause License
|
|
||||||
# cSpell:includeRegExp #.*
|
|
||||||
# cSpell:includeRegExp ("""|''')[^\1]*\1
|
|
||||||
|
|
||||||
module BeefBLUP
|
module BeefBLUP
|
||||||
|
|
||||||
# Import the required packages
|
# Write your package code here.
|
||||||
using CSV
|
|
||||||
using DataFrames
|
|
||||||
using LinearAlgebra
|
|
||||||
using Dates
|
|
||||||
using Gtk
|
|
||||||
|
|
||||||
# Main entry-level function - acts just like the script
|
|
||||||
function beefblup()
|
|
||||||
|
|
||||||
# Ask for an input spreadsheet
|
|
||||||
path = open_dialog_native(
|
|
||||||
"Select a beefblup worksheet",
|
|
||||||
GtkNullContainer(),
|
|
||||||
("*.csv", GtkFileFilter("*.csv", name="beefblup worksheet"))
|
|
||||||
)
|
|
||||||
|
|
||||||
# Ask for an output text filename
|
|
||||||
savepath = save_dialog_native(
|
|
||||||
"Save your beefblup results",
|
|
||||||
GtkNullContainer(),
|
|
||||||
(GtkFileFilter("*.txt", name="Results file"),
|
|
||||||
"*.txt")
|
|
||||||
)
|
|
||||||
|
|
||||||
# Ask for heritability
|
|
||||||
print("What is the heritability for this trait?> ")
|
|
||||||
h2 = parse(Float64, readline(stdin))
|
|
||||||
|
|
||||||
beefblup(path, savepath, h2)
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function beefblup(datafile::String, h2::Float64)
|
|
||||||
# Assume the data is named the same as the file without the trailing extension
|
|
||||||
dataname = join(split(datafile, ".")[1:end - 1])
|
|
||||||
|
|
||||||
# Create a new results name
|
|
||||||
resultsfile = string(dataname, "_results.txt")
|
|
||||||
|
|
||||||
# Pass this info on to the worker
|
|
||||||
beefblup(datafile, resultsfile, h2)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Main worker function, can perform all the work if given all the user input
|
|
||||||
function beefblup(path::String, savepath::String, h2::Float64)
|
|
||||||
|
|
||||||
# Import data from a suitable spreadsheet
|
|
||||||
data = DataFrame(CSV.File(path))
|
|
||||||
|
|
||||||
# Sort the array by date
|
|
||||||
sort!(data, :birthdate)
|
|
||||||
|
|
||||||
# Define fields to hold id values for animals and their parents
|
|
||||||
numanimals = length(data.id)
|
|
||||||
|
|
||||||
# Find the index values for animals and their parents
|
|
||||||
dam = indexin(data.dam, data.id)
|
|
||||||
sire = indexin(data.sire, data.id)
|
|
||||||
|
|
||||||
# Extract all of the fixed effects
|
|
||||||
fixedfx = select(data, Not([:id, :birthdate, :sire, :dam]))[:,1:end - 1]
|
|
||||||
|
|
||||||
# Find any columns that need to be deleted
|
|
||||||
for i in 1:ncol(fixedfx)
|
|
||||||
if length(unique(fixedfx[:,i])) <= 1
|
|
||||||
@warn string("column '", names(fixedfx)[i], "' does not have any unique animals and will be removed from this analysis")
|
|
||||||
DataFrames.select!(fixedfx, Not(i))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Determine how many contemporary groups there are
|
|
||||||
numtraits = ncol(fixedfx)
|
|
||||||
numgroups = ones(1, numtraits)
|
|
||||||
for i in 1:numtraits
|
|
||||||
numgroups[i] = length(unique(fixedfx[:,i]))
|
|
||||||
end
|
|
||||||
|
|
||||||
# If there are more groups than animals, then the analysis cannot continue
|
|
||||||
if sum(numgroups) >= numanimals
|
|
||||||
throw(ErrorException("there are more contemporary groups than animals"))
|
|
||||||
end
|
|
||||||
|
|
||||||
# Define a "normal" animal as one of the last in the groups, provided that
|
|
||||||
# all traits do not have null values
|
|
||||||
normal = Array{String}(undef, 1, numtraits)
|
|
||||||
for i in 1:numtraits
|
|
||||||
for j in numanimals:-1:1
|
|
||||||
if !ismissing(fixedfx[j,i])
|
|
||||||
normal[i] = string(fixedfx[j,i])
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Form the fixed-effect matrix
|
|
||||||
X = zeros(Int8, numanimals, floor(Int, sum(numgroups)) - length(numgroups) + 1)
|
|
||||||
X[:,1] = ones(Int8, 1, numanimals)
|
|
||||||
|
|
||||||
# Create an external counter that will increment through both loops
|
|
||||||
counter = 2
|
|
||||||
|
|
||||||
# Store the traits in a string array
|
|
||||||
adjustedtraits =
|
|
||||||
Array{String}(undef,floor(Int, sum(numgroups)) - length(numgroups))
|
|
||||||
# Iterate through each group
|
|
||||||
for i in 1:length(normal)
|
|
||||||
# Find the traits that are present in this trait
|
|
||||||
localdata = string.(fixedfx[:,i])
|
|
||||||
traits = unique(localdata)
|
|
||||||
# Remove the normal version from the analysis
|
|
||||||
effecttraits = traits[findall(x -> x != normal[i], traits)]
|
|
||||||
# Iterate inside of the group
|
|
||||||
for j in 1:(length(effecttraits))
|
|
||||||
matchedindex = findall(x -> x == effecttraits[j], localdata)
|
|
||||||
X[matchedindex, counter] .= 1
|
|
||||||
# Add this trait to the string
|
|
||||||
adjustedtraits[counter - 1] = traits[j]
|
|
||||||
# Increment the big counter
|
|
||||||
counter = counter + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Create an empty matrix for the additive relationship matrix
|
|
||||||
A = zeros(numanimals, numanimals)
|
|
||||||
|
|
||||||
# Create the additive relationship matrix by the FORTRAN method presented by
|
|
||||||
# Henderson
|
|
||||||
for i in 1:numanimals
|
|
||||||
if !isnothing(dam[i]) && !isnothing(sire[i])
|
|
||||||
for j in 1:(i - 1)
|
|
||||||
A[j,i] = 0.5 * (A[j,sire[i]] + A[j,dam[i]])
|
|
||||||
A[i,j] = A[j,i]
|
|
||||||
end
|
|
||||||
A[i,i] = 1 + 0.5 * A[sire[i], dam[i]]
|
|
||||||
elseif !isnothing(dam[i]) && isnothing(sire[i])
|
|
||||||
for j in 1:(i - 1)
|
|
||||||
A[j,i] = 0.5 * A[j,dam[i]]
|
|
||||||
A[i,j] = A[j,i]
|
|
||||||
end
|
|
||||||
A[i,i] = 1
|
|
||||||
elseif isnothing(dam[i]) && !isnothing(sire[i])
|
|
||||||
for j in 1:(i - 1)
|
|
||||||
A[j,i] = 0.5 * A[j,sire[i]]
|
|
||||||
A[i,j] = A[j,i]
|
|
||||||
end
|
|
||||||
A[i,i] = 1
|
|
||||||
else
|
|
||||||
for j in 1:(i - 1)
|
|
||||||
A[j,i] = 0
|
|
||||||
A[i,j] = 0
|
|
||||||
end
|
|
||||||
A[i,i] = 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Extract the observed data
|
|
||||||
Y = convert(Array{Float64}, data[:,end])
|
|
||||||
|
|
||||||
# The random effects matrix
|
|
||||||
Z = Matrix{Int}(I, numanimals, numanimals)
|
|
||||||
|
|
||||||
# Remove items where there is no data
|
|
||||||
nullobs = findall(isnothing, Y)
|
|
||||||
Z[nullobs, nullobs] .= 0
|
|
||||||
|
|
||||||
# Calculate heritability
|
|
||||||
λ = (1 - h2) / h2
|
|
||||||
|
|
||||||
# Use the mixed-model equations
|
|
||||||
MME = [X' * X X' * Z; Z' * X (Z' * Z) + (inv(A) .* λ)]
|
|
||||||
MMY = [X' * Y; Z' * Y]
|
|
||||||
solutions = MME \ MMY
|
|
||||||
|
|
||||||
# Find the accuracies
|
|
||||||
diaginv = diag(inv(MME))
|
|
||||||
reliability = ones(Float64, length(diaginv)) - diaginv .* λ
|
|
||||||
|
|
||||||
# Find how many traits we found BLUE for
|
|
||||||
numgroups = numgroups .- 1
|
|
||||||
|
|
||||||
# Extract the names of the traits
|
|
||||||
fixedfxnames = names(fixedfx)
|
|
||||||
traitname = names(data)[end]
|
|
||||||
|
|
||||||
# Start printing results to output
|
|
||||||
fileID = open(savepath, "w")
|
|
||||||
write(fileID, "beefblup Results Report\n")
|
|
||||||
write(fileID, "Produced using beefblup (")
|
|
||||||
write(fileID, "https://github.com/millironx/beefblup")
|
|
||||||
write(fileID, ")\n\n")
|
|
||||||
write(fileID, "Input:\t")
|
|
||||||
write(fileID, path)
|
|
||||||
write(fileID, "\nAnalysis performed:\t")
|
|
||||||
write(fileID, string(Dates.today()))
|
|
||||||
write(fileID, "\nTrait examined:\t")
|
|
||||||
write(fileID, traitname)
|
|
||||||
write(fileID, "\n\n")
|
|
||||||
|
|
||||||
# Print base population stats
|
|
||||||
write(fileID, "Base Population:\n")
|
|
||||||
for i in 1:length(normal)
|
|
||||||
write(fileID, "\t")
|
|
||||||
write(fileID, fixedfxnames[i])
|
|
||||||
write(fileID, ":\t")
|
|
||||||
write(fileID, normal[i])
|
|
||||||
write(fileID, "\n")
|
|
||||||
end
|
|
||||||
write(fileID, "\tMean ")
|
|
||||||
write(fileID, traitname)
|
|
||||||
write(fileID, ":\t")
|
|
||||||
write(fileID, string(solutions[1]))
|
|
||||||
write(fileID, "\n\n")
|
|
||||||
|
|
||||||
# Contemporary group adjustments
|
|
||||||
counter = 2
|
|
||||||
write(fileID, "Contemporary Group Effects:\n")
|
|
||||||
for i in 1:length(numgroups)
|
|
||||||
write(fileID, "\t")
|
|
||||||
write(fileID, fixedfxnames[i])
|
|
||||||
write(fileID, "\tEffect\tReliability\n")
|
|
||||||
for j in 1:numgroups[i]
|
|
||||||
write(fileID, "\t")
|
|
||||||
write(fileID, adjustedtraits[counter - 1])
|
|
||||||
write(fileID, "\t")
|
|
||||||
write(fileID, string(solutions[counter]))
|
|
||||||
write(fileID, "\t")
|
|
||||||
write(fileID, string(reliability[counter]))
|
|
||||||
write(fileID, "\n")
|
|
||||||
|
|
||||||
counter = counter + 1
|
|
||||||
end
|
|
||||||
write(fileID, "\n")
|
|
||||||
end
|
|
||||||
write(fileID, "\n")
|
|
||||||
|
|
||||||
# Expected breeding values
|
|
||||||
write(fileID, "Expected Breeding Values:\n")
|
|
||||||
write(fileID, "\tID\tEBV\tReliability\n")
|
|
||||||
for i in 1:numanimals
|
|
||||||
write(fileID, "\t")
|
|
||||||
write(fileID, string(data.id[i]))
|
|
||||||
write(fileID, "\t")
|
|
||||||
write(fileID, string(solutions[i + counter - 1]))
|
|
||||||
write(fileID, "\t")
|
|
||||||
write(fileID, string(reliability[i + counter - 1]))
|
|
||||||
write(fileID, "\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
write(fileID, "\n - END REPORT -")
|
|
||||||
close(fileID)
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,2 +1,6 @@
|
||||||
using BeefBLUP
|
using BeefBLUP
|
||||||
using Test
|
using Test
|
||||||
|
|
||||||
|
@testset "BeefBLUP.jl" begin
|
||||||
|
# Write your tests here.
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in a new issue