Skip to content

Add repeated-candidate condense cleaner - #385

Open
fetachino wants to merge 1 commit into
mggg:mainfrom
fetachino:feat/remove-repeated-candidates
Open

fetachino wants to merge 1 commit into
mggg:mainfrom
fetachino:feat/remove-repeated-candidates

Conversation

@fetachino

Copy link
Copy Markdown

Summary

  • add a public rank-profile cleaner that removes repeated candidates and condenses gaps in one pass
  • preserve altered/unaltered ballot metadata, including the existing trailing-gap equivalence behavior
  • export the new cleaner through votekit.cleaning
  • cover duplicate candidates, ties, internal gaps, sequential equivalence, and trailing empty positions

Why

Callers currently need to chain duplicate removal and condensation, creating an intermediate cleaned profile. This adds the combined operation requested in #275, following the existing optimized remove-and-condense pattern.

Validation

  • py -m uv run pytest tests/cleaning/rank_profiles (19 passed)
  • py -m uv run ruff check src/votekit/cleaning tests/cleaning/rank_profiles/test_remove_repeat_cands_and_condense_rank_profile.py
  • py -m uv run ruff format --check src/votekit/cleaning tests/cleaning/rank_profiles/test_remove_repeat_cands_and_condense_rank_profile.py
  • py -m uv run ty check src/votekit/cleaning/rank_profiles_cleaning.py

Closes #275.

@fetachino
fetachino marked this pull request as ready for review August 10, 2026 20:41
@peterrrock2

Copy link
Copy Markdown
Collaborator

Hey @fetachino!

Thank you so much for contributing to VoteKit! It's wonderful to see our work being used by people outside of our community. If you happen to have a moment, I would love to hear more about how you found the package and what you are using it for so that we can better fit the needs of our users.

You may have noticed that we made our latest release of VoteKit around two weeks ago. Since then, I have been preoccupied with some other projects and missed the latest set of PRs that came in. This looks like a great start on this issue, and one of the other maintainers will be adding a review with some additional feedback shortly.

The following section may or may not pertain to the work that you have done here, but, since we have had a small rush of PRs from new contributors in recent days, I thought it a good idea to include this message on all of the recent PRs.


Additional notes (this section is a form that I am including on several recent PRs)

Since there have been so many new contributors in the last couple of weeks (a wonderful problem to have), I thought it important to update the "Use of AI Tools" section of the Contributing.md on our main branch to clarify what we currently consider to be best-practices for our lab.

To be clear, this this is not an indictment of the use of any AI tools or a statement about the work presented here (indeed, it would be a bit illogical to find anyone at issue with this policy before it was explicitly stated!). I include it here only because this is 1. a recent update in a document people infrequently visit and 2. an effort to clearly communicate our expectations around contributing to this project.

I'll include the exact snippet here so you don't have to go through the trouble of finding it yourself:

Use of AI tools

Researchers rely on VoteKit and its results, so contributors have a heightened responsibility for
the correctness and clarity of every change.

  • Core implementation and business logic in src/votekit/ must be written by humans. AI tools may
    provide inline assistance (e.g., an editor copilot) and may be used to navigate and deepen
    understanding of the codebase and domain material (e.g., with chat agents). Contributors should
    not delegate the generation or rewriting of core implementation code to agentic coding tools.
    For example, Codex GUI/CLI, Claude GUI/CLI, Cursor v3+, and similar tools should not be asked to
    generate or substantially rewrite that code.
  • Tests may be developed jointly by humans and AI. AI is particularly useful for adversarial
    testing, such as searching for user inputs that break an implementation, but the person
    responsible for the pull request must review every AI-generated or AI-assisted test and confirm
    that it meaningfully tests the intended behavior.
  • Docstrings may be developed jointly by humans and AI. The person responsible for the pull
    request must read, edit, and review every AI-assisted docstring for accuracy, thoroughness, and
    compliance with the documentation and formatting guidelines below.

The person responsible for a pull request remains accountable for all of its contents, regardless
of which tools assisted in preparing it.


Thank you again for your contribution (and for looking at #383!!), and I hope that we'll see more from you soon!

-Peter
(Lead Maintainer of VoteKit)

@graceg571 graceg571 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good! Good call to use the original profile's candidates list regardless of retain_original_candidate_list's setting. Just 2 comments. 1 about adding an additional test and another about writing your own _is_equiv function.

Comment on lines +239 to +241
if tuple(ranking_df.loc[i])
== remove_repeat_cands_from_ranking_row(tuple(ranking_df.loc[i]))
and _is_equiv_to_condensed(ranking_df.loc[i]) # type: ignore[arg-type]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Move to its own function that can call _is_equiv_to_condensed internally. Follows the pattern of other functions that condense ballots and clarifies what ballots are altered versus unaltered via your cleaning function.

profile = RankProfile(
ballots=[
RankBallot(ranking=[{"A"}, {"A"}, {"B"}, {"C"}]),
RankBallot(ranking=[{"A", "C"}, {"C"}, frozenset(), {"B"}]),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Add a case where the repeated candidate is in a tie. Not just the first instance of a candidate within a ranking.

@peterrrock2

Copy link
Copy Markdown
Collaborator

@fetachino just following up to see if you saw the above messages. Thanks!

@peterrrock2

Copy link
Copy Markdown
Collaborator

Continuing the conversation from #383.

Hi Peter, thanks for reaching out and for clarifying the updated AI-tools policy. I found VoteKit while looking for tools and patterns for working with ranked-choice election data. I am using it to contribute cleaning utilities that simplify repeated-candidate removal and rank-profile condensation, which led to PR #385. I appreciate the maintainers work and will make sure future contributions >follow the projects expectations around implementation, tests, and documentation.

Like I said, I would love to hear more about the work that you are doing whenever you get a chance!

Also a clarification @fetachino, does this mean that you intend to continue to work on this PR? It would seem so, and I would love to have your contribution, but I want to make sure. Let me know!

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.

remove_repeated_candidates and condense

3 participants