Source code for bpf90tools.tools._blupf90

#!/usr/bin/env python
# coding: utf-8

# Copyright (C) 2025-2026 Igor Loschinin.
# Distributed under the lgplv3 software license, see the accompanying
# Everyone is permitted to copy and distribute verbatim copies
# of this license document, but changing it is not allowed.

__author__ = "Igor Loschinin (igor.loschinin@gmail.com)"
__all__ = ("Blupf90", )

from pathlib import Path

import pandas as pd

from ._if90 import If90
from .settings import (
	BLUPF90,
	RENF90_PAR,
	SOLUTIONS,
	RENADD
)
from ..utils import (
	run_app,
	CheckMixin,
	transform
)
from ..parsers import (
	PSolution,
	PPed
)


[docs] class Blupf90(If90, CheckMixin): """ BLUP (Best Linear Unbiased Prediction - the best linear unbiased prediction) method for determining the genetic potential of animals. """ def __init__( self, *, app: str, work_dir: str | Path, vara: float | None = None, fn_par: str | None = None ) -> None: """ :param app: The name of the program. :param work_dir: Directory where all programs and files are located. :param vara: Genetic variant. None by default. Used to translate s.e. to reliability. :param fn_par: The name of the parameter file with settings. """ If90.__init__(self, app=app, work_dir=work_dir, fn_par=fn_par) self.__vara = vara self.__data = None @property def evaluation(self) -> pd.DataFrame | None: return self.__data
[docs] def run(self) -> bool: """ Starts breeding value calculation. :return: Returns true if the program started and ran without errors else false. :raise: Exceptions when files do not exist. """ if isinstance(self._work_dir, str): self._work_dir = Path(self._work_dir) if not self._work_dir.is_absolute(): self._work_dir = self._work_dir.absolute() if not self.is_dir(self._work_dir): raise OSError("Directory does not exist.") if not self.__blup(): return False if not self.__processing_result(self._work_dir): return False return True
def __blup(self) -> bool: """ Starting the breeding value calculation. """ # Define file param if self._par_file is not None: _config = self._work_dir / self._par_file else: _config = self._work_dir / RENF90_PAR if not self.is_file(_config): raise OSError(f"{RENF90_PAR} file is not found. Run renumf90.") # Run app if self._app != transform(BLUPF90): raise ValueError(f"The program being run is not {self._app}.") _app_file = self._work_dir / self._app if not run_app(_app_file, _config, dir_cwd=self._work_dir): return False return True def __processing_result(self, work_dir: Path) -> bool: """ Processing data. :param work_dir: Directory where all programs and files are located. :return: Return true if processing data successful else False. """ f_solutions = work_dir / SOLUTIONS if not self.is_file(f_solutions): raise OSError(f"{SOLUTIONS} file is not found.") f_renadd = self.__search_ped(work_dir) data_ped = self.__handler_ped(f_renadd) data_sol = self.__handler_sol(f_solutions) self.__data = data_ped.merge( data_sol, left_on="nomer", right_on="level" ).drop(columns=["level", "nomer"]) return True def __handler_sol(self, pth_file: Path) -> pd.DataFrame: """ Results file handler with breeding value and estimation accuracy. :param pth_file: Path to file solutions. :return: Return dataframe with data - ebv, se or rel. """ sol = PSolution(varg=self.__vara) sol.parse_file(pth_file) return sol.solutions @staticmethod def __handler_ped(pth_file: Path) -> pd.DataFrame: """ Ped file handler. :param pth_file: Path to file ped. :return: Return dataframe with data - [nomer, id]. """ ped = PPed() ped.parse_file(pth_file) return ped.values @staticmethod def __search_ped(w_d: Path) -> Path | None: """ Search for a pedigree file. :param w_d: Search directory. :return: Returns either the file if found, or non if not found. """ lst_files = [] for item_f in w_d.glob("*"): lst_files.extend(RENADD.findall(item_f.name)) if not lst_files or len(lst_files) > 1: return None return w_d / lst_files[0]