Metadata-Version: 2.4
Name: setalign
Version: 0.1.0
Summary: Information-theoretic citation alignment metrics for comparing citation sets between actors.
Author: Mauricio Mandujano Manriquez
License-Expression: MIT
Project-URL: Homepage, https://github.com/mauriciomm7/setalign
Project-URL: Repository, https://github.com/mauriciomm7/setalign
Project-URL: Issues, https://github.com/mauriciomm7/setalign/issues
Keywords: citation-analysis,information-theory,set-similarity,judicial-politics,bibliometrics
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pandas>=1.5
Requires-Dist: pyarrow>=10.0
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"
Dynamic: license-file

# ``setalign``

`setalign` provides a toolkit for analyzing **citation alignment** between agents (e.g., courts, institutions, documents) using **information-theoretic principles**.  

## 🎯 Motivation

The idea for this project arose from the analytical need of comparing citations between institutional actors. As readers of judicial politics will know, politics happens through law, and citations to caselaw has been widely used to scaled judical opinions (see works by Jeffrey Lax, Jonatha Kastellec, Erik Voeten, Daniel Naurin for instance). However, a uncharted territory is how to robustly compare citation set's between agents when you expect them to be very similar, if not identical. At face value, this seems like a non-issue all that you do is count overlaps and that will easily tell you the degree of precisely, _overlap_. Yet, what about when the second mover overlaps entirely with the first one but also adds an extra citation? That should not be scaled equally as one with a perfect one-to-one overlap, at least not so if we are saying that _legal ciations matter_.

`AGENT_1 = {A,B,C,D}` `AGENT_2 = {Z,Y,X,W}`  

`AGENT_1 = {A,B,C,D}` `AGENT_2 = {A,B,C,D,E}`



## 🧮 Core Methodology

This module integrates two methodological innovations:

1. **Set Citation Alignment Ratio (SCAR)** — measures informational agreement between citation sets.  
2. **Locally Information-Adjusted Citations (LIAC)** — transforms SCAR into interpretable, scalable alignment units.  

### Method Overview

1. Applies **information penalties** for both missing and extra citations.  
2. Scales by **local citation universe size** to enable cross-case comparability.  
3. Produces **bounded, interpretable measures** ranging from *–γ* to *+γ*.  

## 🧠 Primary Use Cases

- Judicial citation behavior (ideological alignment, institutional influence)  
- Legislative or policy consensus measurement  
- Academic citation analysis (political science, law, etc.)  
- Cross-institutional alignment studies  
- Policy or regulatory document concordance

## ⚙️ Walkthrough With Example Data

The example dataset contains paired citation lists for two actors. Each row is a dyad-level comparison.

```python
from setalign import (
    calculate_liac,
    calculate_scar,
    calculate_scar_metrics,
    extract_citation_sets,
    load_ecj_example_data,
    scale_scar_by,
)

# Load the packaged ECJ example data
ecj_data = load_ecj_example_data()
```

The citation columns must contain list-like citation sets. In this example:

- `actor_a_citations` is the reference or agenda-setting actor.
- `actor_b_citations` is the responding actor.
- `dyad_id` identifies the paired comparison.

The packaged example dataset is intentionally minimal. It contains only the columns used in this walkthrough: `dyad_id`, `actor_a_citations`, and `actor_b_citations`.

### Calculate SCAR

`calculate_scar()` compares the two citation lists and adds:

- `case_flag`: the set relationship case.
- `scar`: the Set Citation Alignment Ratio.

Semantically, SCAR asks how much informational agreement exists between the reference actor's citation set and the responding actor's citation set. It is not only a raw overlap count. Missing citations and additional citations both matter because each changes the information contained in the second actor's response.

```python
para_level_data = calculate_scar(
    ecj_data,
    input_col1="actor_a_citations",
    input_col2="actor_b_citations",
)
```

### Add Supporting Citation Metrics

`calculate_scar_metrics()` adds interpretable set counts, including the intersection size, overall difference, initial citation universe size, and joint citation universe size.

These columns make the SCAR score auditable. For example, `intersection` tells you how many citations both actors share, while `overall_difference` tells you how much citation material appears in only one actor's set. `init_citation_universe_len` is especially important because it records how much citation information actor A placed on the table.

```python
para_level_data = calculate_scar_metrics(
    para_level_data,
    input_col1="actor_a_citations",
    input_col2="actor_b_citations",
)
```

### Extract Citation Sets

`extract_citation_sets()` adds the actual citation sets behind the metrics:

- `shared_citations_list`
- `agent1_unique_citations_list`
- `agent2_unique_citations_list`
- `universe_citations_list`

This step is useful when you want to move from measurement back to interpretation. Instead of only knowing that two actors diverged, you can inspect which citations were shared, which were unique to actor A, and which were introduced by actor B.

```python
para_level_data = extract_citation_sets(
    para_level_data,
    input_col1="actor_a_citations",
    input_col2="actor_b_citations",
)
```

### Calculate LIAC

`calculate_liac()` transforms SCAR into Locally Information-Adjusted Citations. The default scaling term is the initial citation universe size, here stored in `init_citation_universe_len`.

The intuition is local comparability. A SCAR score of `0.50` does not mean the same thing when actor A cited one authority as when actor A cited twenty. LIAC scales the alignment score by the size of actor A's citation universe, so the final value reflects both alignment quality and the amount of citation information at stake in that dyad.

```python
para_level_data["liac"] = calculate_liac(
    para_level_data,
    init_citation_universe_len="init_citation_universe_len",
)
```

You can also scale SCAR by a non-local value using `scale_scar_by()`. This is useful when you have theoretical priors about the citation universe that should anchor comparisons across rows. For example, a global median scaling term treats the typical citation-universe size as the reference point, rather than letting each dyad define its own scale. This is less naive than choosing an arbitrary constant, but it is still a modeling choice: use it when the global reference value has a substantive interpretation for your application.

```python
para_level_data["liac_global_median"] = scale_scar_by(
    para_level_data,
    scar_col="scar",
    scaling_term=para_level_data["init_citation_universe_len"].median(),
)
```

### Inspect Results

```python
result_cols = [
    "dyad_id",
    "case_flag",
    "scar",
    "intersection",
    "overall_difference",
    "init_citation_universe_len",
    "joint_citation_universe_len",
    "liac",
    "liac_global_median",
]

print(para_level_data[result_cols].head(8))
```

Example output, shown in a compact -style layout:

```text
Rows: 8
Columns: 9

dyad_id                       DYAD001, DYAD002, DYAD003, DYAD004, DYAD005, DYAD006, DYAD007, DYAD008
case_flag                     2, 1, 1, 1, 1, 2, 2, 3
scar                          0.250, 0.333, 0.333, 0.333, 1.000, 0.333, 0.333, 0.019
intersection                  1, 1, 1, 1, 1, 1, 1, 0
overall_difference            3, 2, 2, 2, 0, 2, 2, 2
init_citation_universe_len    2, 1, 1, 1, 1, 2, 2, 1
joint_citation_universe_len   4, 3, 3, 3, 1, 3, 3, 2
liac                          -1.000, -0.334, -0.334, -0.334, 1.000, -0.668, -0.668, -0.962
liac_global_median            -0.500, -0.334, -0.334, -0.334, 1.000, -0.334, -0.334, -0.962
```

## 🎓 Citation

If you use this framework in academic research, please cite:

Mandujano Manríquez, M. (2026). setalign: Information-Theoretic Citation Alignment Module. Github: https://github.com/mauriciomm7/setalign

```bliblatex
@misc{mandujano2026setalign,
  author       = {Mauricio Mandujano Manríquez},
  title        = {setalign: Information-Theoretic Citation Alignment Module},
  year         = {2026},
  howpublished = {\url{https://github.com/mauriciomm7/setalign}},
  note         = {GitHub repository}
}
```

## 📄 License

This project is licensed under the [MIT License](./LICENSE).

## ✅ TODO

- [ ] UPDATE `caculate_scar` such that it lets you decide who is the "referenece"  agent A or B or 
create equivalent function but flip case one to zeroes.

### READ References

Information Theory:

- [ ] Shannon, Claude E. (1948). "A Mathematical Theory of Communication." Bell System Technical Journal, 27(3), 379-423.
- [ ] Leydesdorff, Loet. Various works on information-theoretic approaches to citation analysis - His theoretical framework treats citations as selections operating in complex information systems

Bibliometrics:

- [ ] Lam, Weng Hoe, et al. (2022). "Bibliometric Analysis of Information Theoretic Studies." Entropy, 24(10), 1359 - Shows the growing application of information theory across disciplines
- [ ] Garfield, Eugene (1996). Multiple works on citation analysis fundamentals and strategic aspects
- [ ] Wouters, Paul (1997). "Citation cultures" - Addresses how citation practices vary and can be strategic
- [ ] Validation of Google Scholar as an Impact Measure for Political Science
- [ ] Hill, K.Q. (2022). "Web of Science Book Citation Indices and the Representation of Political Science." Journal of Electronic Publishing - Addresses unique citation challenges in political science.

Mathematical Framework:

- [X] Jaccard, Paul (1912) - For set similarity measures (Jaccard coefficient) as a baseline comparison
