Skip to content

New labelling system#115

Open
BadPrograms wants to merge 4 commits into
v4.xfrom
new_labelling_system
Open

New labelling system#115
BadPrograms wants to merge 4 commits into
v4.xfrom
new_labelling_system

Conversation

@BadPrograms

Copy link
Copy Markdown
Collaborator

Labels are now a new class that works like a dict, but tests if all values of the dictionary are actually strings, can be labels. These labels are saved inside a new attribute of LineageTree called labelling.
lT.labelling contains all the labels and tests if all new labels correspond to a subset of the nodes or the set of the nodes.
To access these labels:

lT = ...
lT.labelings.list_of_labels
# ["default_dict", "label1", "label2",...]
lT.labellings["default_dict"][node_id]
# prints the label of the node

get_ancestor_with_attribute works the same way as before.
get_labelled_ancestor works the same way as before.

I am open to discuss if we should drop labelling and save everything directly on LineageTree.

@BadPrograms BadPrograms requested a review from leoguignard July 1, 2026 11:34
Comment thread src/lineagetree/_labelling.py Outdated
data_type: Any = None,
) -> None:
if not data and data_type is None:
raise ValueError("data_type cant be `None` if data is empty.")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

typo

Comment thread src/lineagetree/_labelling.py Outdated

def __setitem__(self, key: Any, item: Any) -> None:
if not isinstance(item, self.data_type):
raise TypeError(f"All values must be {self.data_type}")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Usually you specify which data was the input

super().__init__(iterable, str)


class Labelling:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you do the docstring please?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is a Labelling object?

Comment thread src/lineagetree/_labelling.py Outdated
Comment on lines +53 to +55
l = Labels(value)
if not value:
return

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is that order correct?

You return nothing and not doing anything, is that on purpose?

Comment on lines +56 to +59
if not set(l).issubset(self._nodes):
raise ValueError(
"All ids in the labelset should correspond to ids in the LineageTree object."
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Does it create errors if you have keys that are not in the set of nodes?
If yes which ones?
If no why do you protect for it?

Comment thread src/lineagetree/_labelling.py Outdated

if data_type is None:
if not isinstance(data, Mapping):
tmp_d = tuple(data)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That's really not optimal, you transform the whole dataset as a tuple to get the first element.

Maybe:

value = next(iter(data))
if len(value) != 2:
    raise ValueError(f"`data` could not be converted to dict.")
else:
    self.data_type = type(next(iter(data))[1])

Comment thread src/lineagetree/_labelling.py Outdated
def __init__(
self,
data: Iterable,
data_type: Any = None,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Aren't you expecting a type rather than Any?

Comment thread tests/test_lineageTree.py
Comment on lines -719 to +725
def get_norm(self, root) -> int:
return len(
self.lT.get_all_chains_of_subtree(root, end_time=self.end_time)
)
# def get_norm(self, root) -> int:
# return len(
# self.lT.get_all_chains_of_subtree(root, end_time=self.end_time)
# )

assert lt.unordered_tree_edit_distance(
176, 29345, style=new_tree
) == lt.unordered_tree_edit_distance(176, 29345, style="normalized_simple")
# assert lt.unordered_tree_edit_distance(
# 176, 29345, style=new_tree
# ) == lt.unordered_tree_edit_distance(176, 29345, style="normalized_simple")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why removing these tests?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

the uted pr is gonna be applied soon, so I let them there to remember to reimplement them after uted (Uted needs this PR to become final)


@property
def list_of_labels(self):
return [k for k, v in self.__dict__.items() if isinstance(v, Labels)]

@leoguignard leoguignard Jul 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't like the idea of a default dict but if you have one I think you should have something like:

def __getitem__(self, key):
    return self.default_dict[key]

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

interesting idea lets discuss that


class Labelling:

default_dict = {}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

SHouldn't it be None?

Comment thread src/lineagetree/_labelling.py Outdated


class StaticTypedValueDict(UserDict):
"""Dict that allows only one type of values."""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also, why is this declared in a _labelling.py file? Won't it be used for other types of data?

Comment thread src/lineagetree/_labelling.py Outdated
self,
data: Iterable,
data_type: Any = None,
) -> None:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think it would be nice if you also could specify a default return value.
Like for labels it could be "".

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.97297% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.41%. Comparing base (8263d4d) to head (e9f309b).

Files with missing lines Patch % Lines
src/lineagetree/util_types.py 57.89% 8 Missing ⚠️
src/lineagetree/plot.py 25.00% 6 Missing ⚠️
src/lineagetree/_core/_navigation.py 75.00% 3 Missing ⚠️
src/lineagetree/_labelling.py 90.90% 2 Missing ⚠️
src/lineagetree/lineage_tree.py 92.30% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             v4.x     #115      +/-   ##
==========================================
- Coverage   87.16%   83.41%   -3.75%     
==========================================
  Files          20       22       +2     
  Lines        1932     1960      +28     
==========================================
- Hits         1684     1635      -49     
- Misses        248      325      +77     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants