Skip to content

Find grains and mappings

Does each exam belong to one site? Use df and fw from Getting started to check whether repeated records for an exam agree about its site.

dependencies = fw.discover_dependencies(
df,
features=["exam", "site"],
max_key_size=1,
)
candidates = dependencies.to_frame("candidates")
print(
candidates[
["columns", "groups", "repeated_groups", "determines"]
].to_string(index=False)
)
columns groups repeated_groups determines
[exam] 3 3 [site]
[site] 2 2 []

A candidate is a column, or combination of columns, that might identify a useful group. Here each exam occurs twice. A unique row identifier would also determine the other fields, but it would tell us nothing about agreement across repeated records.

Read the findings to see which mappings hold:

findings = dependencies.to_frame("findings")
print(findings[["id", "statement"]].to_string(index=False))
id statement
f0 exam determines site

Exam determines site: every row for exam 1 says North, as does every row for exam 2, while exam 3 belongs to South. Site does not determine exam because North contains two exams.

Read the target-specific evidence, especially when attributes are missing:

support = dependencies.to_frame("dependencies")
print(
support[
["determinant", "target", "target_coverage", "repeat_coverage", "repeat_modal_accuracy"]
].to_string(index=False)
)
determinant target target_coverage repeat_coverage repeat_modal_accuracy
[exam] site 1.0 1.0 1.000000
[site] exam 1.0 1.0 0.666667

Here every exam and site is observed, and all tested groups repeat. In a sparse table, excluding missing targets can leave only singleton groups even when the candidate repeated in the original table. determines_with_repeated_support lists global exact targets that still have a repeated group after exclusions. Check global_targets_tested against global_targets_possible before interpreting an empty list. The sparse-target example shows why exactness alone does not establish repeated agreement.

When you already know the candidate keys, use grain to evaluate them directly:

exam_grain = fw.grain(df, candidate_keys=["exam"])
print(fw.render_plaintext(exam_grain))
Fieldwork · grain
Grain (computed)
Candidate keys
exam: repeated grouping
Observed dependencies among tested keys.
exam [exam] -> site
observed dependency holds; 0/3 violating groups; 0 affected rows
support: 6 rows; 0 singleton, 3 repeated groups
exam [exam] -> image_1
observed dependency fails; 3/3 violating groups; 6 affected rows
support: 6 rows; 0 singleton, 3 repeated groups
exam [exam] -> image_2
observed dependency fails; 3/3 violating groups; 6 affected rows
support: 6 rows; 0 singleton, 3 repeated groups
site: coarsest observed candidates: exam
image_1: grain undetermined by supplied keys
image_2: grain undetermined by supplied keys

If exam numbers restart at each site, declare a two-column key with fw.KeySpec("site_exam", ("site", "exam")). This treats site and exam together as the identifier.

Larger datasets often have mappings that almost hold. discover_dependencies reports modal accuracy: the fraction of rows agreeing with the most common value in their group. For example, if 19 of 20 records agree, the accuracy is 95%.

Choose the relevant finding ID from the findings table, then pass it to dependencies.inspect(df, finding_id, exceptions=True, all_matches=True). The returned rows disagree with their group’s most common value; check the source before deciding whether they are errors.

The warehouse walkthrough works through this process, including a case where two columns are needed to identify a group. The argument reference covers search limits, missing values, and comparisons within contexts.