1
0
Fork 0
mirror of https://github.com/MillironX/beefblup.git synced 2024-11-13 03:03:08 +00:00

Run Julia template engine for CI purposes

This commit is contained in:
Thomas A. Christensen II 2021-06-19 19:16:34 -05:00
parent 95fd34ce3a
commit 0a537f9928
Signed by: millironx
GPG key ID: 139C07724802BC5D
8 changed files with 60 additions and 352 deletions

View file

@ -1,6 +1,6 @@
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.
Redistribution and use in source and binary forms, with or without

View file

@ -1,12 +1,13 @@
name = "BeefBLUP"
uuid = "03993faf-e476-444a-86c9-f31e8122fa24"
authors = ["Thomas A. Christensen II <25492070+MillironX@users.noreply.github.com>"]
version = "0.3.0"
uuid = "4c4dd571-0773-455e-9a95-72727008e3f4"
authors = ["Thomas A. Christensen II <25492070+MillironX@users.noreply.github.com> and contributors"]
version = "0.1.0"
[deps]
ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63"
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Gtk = "4c0ca9eb-093a-5379-98c5-f87ac0bbbf44"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
[compat]
julia = "1.3"
[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[targets]
test = ["Test"]

View file

@ -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)
[![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
[![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)

3
docs/Project.toml Normal file
View file

@ -0,0 +1,3 @@
[deps]
BeefBLUP = "4c4dd571-0773-455e-9a95-72727008e3f4"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"

24
docs/make.jl Normal file
View 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
View 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
View 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
# Import the required packages
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)
# Write your package code here.
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

View file

@ -1,2 +1,6 @@
using BeefBLUP
using Test
@testset "BeefBLUP.jl" begin
# Write your tests here.
end