Implementa pipeline de geração de árvore filogenética em formato Newick - #15
Conversation
… pelo algoritmo, finalizada
…struir a árvore filogenética
…s no formato Newick
… em formato Newick
…ento de erros e retorna o caminho do arquivo gerado. Adiciona arquivo de saída com a árvore filogenética em formato Newick.
…es Neighbor-Joining, incluindo a carga de matriz de distâncias, construção da árvore, salvamento em formato Newick e geração de relatório em JSON para satisfazer o contrato.
…ndo no construtor de árvores Neighbor-Joining.
… com detalhes da árvore construída
…e árvores Neighbor-Joining, incluindo carregamento da matriz de distâncias, construção da árvore e geração de relatório de sucesso.
…de árvores Neighbor-Joining
… comando de execução em um script prático, para rodar com ./script.sh
…rquivo Newick com a árvore filogenética gerada.
There was a problem hiding this comment.
Pull request overview
Adds a new damicore_tree_builder model that builds a phylogenetic tree from a distance-matrix CSV using BioPython's Neighbor-Joining implementation and writes the result in Newick format. The pipeline is split into a loader/validator, an NJ builder, a Newick writer, and a CLI that prints a JSON success/error report, plus a helper shell script and a sample output fixture.
Changes:
- New modules
matrix_loader.py,neighbor_joining.py,newick_writer.pyimplementing the three pipeline stages as classes. - New
cli.py(argparse-based) plusscript.shwrapping the end-to-end run. - New fixture
output-phylo-tree-...newickcontaining a generated tree.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
models/damicore_tree_builder/src/damicore_tree_builder/matrix_loader.py |
CSV loader with shape/label/numeric/diagonal/symmetry/non-negativity validation. |
models/damicore_tree_builder/src/damicore_tree_builder/neighbor_joining.py |
Converts full matrix to BioPython lower-triangular form and runs DistanceTreeConstructor.nj. |
models/damicore_tree_builder/src/damicore_tree_builder/newick_writer.py |
Persists the resulting tree to a .newick file via Phylo.write. |
models/damicore_tree_builder/src/damicore_tree_builder/cli.py |
Argparse CLI orchestrating loader → builder → writer, emitting JSON report. |
models/damicore_tree_builder/src/damicore_tree_builder/script.sh |
Convenience shell wrapper that cds into the package and runs the CLI. |
models/fixtures/dataset-seade-pop-age/output-phylo-tree-...newick |
Sample generated Newick tree checked in alongside the code. |
Comments suppressed due to low confidence (3)
models/damicore_tree_builder/src/damicore_tree_builder/matrix_loader.py:28
DEFAULT_INPUT_PATHis an absolute path (/models/...) rooted at the filesystem root rather than the repo. InstantiatingMatrixLoader()without arguments will always raiseFileNotFoundError. Since the CLI already requires--input, remove this default entirely and makeinput_patha required parameter, or compute the path relative to the repo.
DEFAULT_INPUT_PATH = "/models/fixtures/dataset-seade-pop-age/output-distance-ncd-gzip-cod_distr-ano-idade.csv"
models/damicore_tree_builder/src/damicore_tree_builder/matrix_loader.py:50
- The repo's conventions are function-first: classes should only be
@dataclass(frozen=True)data containers orProtocolimplementations.MatrixLoader,NeighborJoining, andNewickWriterare stateless wrappers around a single operation and should be plain module-level functions (e.g.,load_matrix(path),build_nj_tree(names, matrix),write_newick(tree, path)), with private helpers prefixed_. This also removes the redundant__init__/state and simplifies testing.
class MatrixLoader:
"""
Loads and validates a distance matrix from a CSV file.
The MatrixLoader class reads a CSV file where rows and columns are labeled
with the same element names. It extracts the element names, converts the
distance values to floats, and validates that the matrix is suitable for
the Neighbor-Joining algorithm.
The validation checks whether:
- the matrix is square;
- row labels and column labels match;
- the diagonal values are zero or close to zero;
- the matrix is symmetric;
- all distance values are numeric;
- there are no negative distances.
After validation, the class returns the element names and the distance
matrix in a format that can be used by the tree-building algorithm.
"""
models/damicore_tree_builder/src/damicore_tree_builder/matrix_loader.py:104
_validate_numeric_valuescallsdataframe.astype(float)and discards the result, so it only serves as validation — but then_validate_dataframeimmediately callsdataframe.to_numpy(dtype=float), which performs the same conversion a second time and raises on failure. The first call is therefore a redundant full-matrix pass. Either remove_validate_numeric_valuesand rely on theto_numpy(dtype=float)conversion (wrapping it to raise a clearerValueError), or use the converted result instead of doing the work twice.
self._validate_numeric_values(dataframe)
matrix = dataframe.to_numpy(dtype=float)
self._validate_non_negative_distances(matrix)
self._validate_zero_diagonal(matrix)
self._validate_symmetric_matrix(matrix)
…zia e melhora a tipagem
There was a problem hiding this comment.
A classe MatrixLoader deve ser substituída por funções. Conforme as convenções do projeto, classes são permitidas apenas como @dataclass(frozen=True) ou implementações de Protocol — este caso não é nenhum dos dois. O padrão de uso MatrixLoader(path).load() é uma função disfarçada de classe; input_path pertence como parâmetro, não como estado de instância.
Outros problemas:
List,Tupledotyping→ usar built-inslist[...],tuple[...](Python 3.12)- Docstrings no estilo Google (
Args:,Returns:) → obrigatório estilo NumPy - Bloco
if __name__ == "__main__":deve ser removido; código de teste pertence atests/ - Comentários de seção (
# Libraries,# Builder,# ----) explicam o quê, não por quê — remover
Correção: expor load_matrix(input_path: Path) -> tuple[list[str], list[list[float]]] como ponto de entrada público, com os métodos privados atuais como helpers de módulo prefixados com _, recebendo argumentos explícitos.
There was a problem hiding this comment.
veja outros arquivos que possam ter o mesmo problema e alterar para programação funcional
There was a problem hiding this comment.
o cli.py já irá fazer isso, não precisamos desse arquivo script.sh
Adiciona o módulo damicore_tree_builder, responsável por gerar árvores filogenéticas a partir de uma matriz de distâncias em CSV usando o algoritmo Neighbor-Joining.
O fluxo principal foi organizado em módulos com responsabilidades separadas:
OBS: Não executei testes automatizados ainda.