Skip to content

Vote Type Classifications (Stemmetype)

The Danish Parliamentary vote type system provides a complete and precise framework for recording individual voting behavior in parliamentary decisions. With 4 distinct vote classifications, this system captures every possible voting choice available to members of parliament, including the critical distinction between absence and abstention.

Overview

Each individual vote (Stemme) cast by a parliamentary actor is classified according to one of four standardized vote types. This classification system has remained stable since the API's inception, providing consistent categorization for voting analysis across all parliamentary periods.

Key Characteristics: - 4 comprehensive vote types - Complete coverage of all voting choices - Absence vs Abstention distinction - Differentiates non-participation from deliberate abstention - Binary opposition structure - Clear For/Against choices with neutral alternatives - Universal applicability - Used across all voting procedures and session types - Historical consistency - Stable classification since 2014

Complete Stemmetype Reference

1. For (In Favor)

ID: 1
Danish: For
English: For/In Favor/Yes
Meaning: Member voted in support of the proposal, motion, or question
Parliamentary Context: Positive endorsement of the matter under consideration
Statistical Weight: +1 in favor calculations

Usage Patterns: - Primary choice for supporting legislation - Endorsement of amendments and modifications
- Agreement with committee recommendations - Support for procedural motions

2. Imod (Against)

ID: 2
Danish: Imod
English: Against/Opposed/No
Meaning: Member voted in opposition to the proposal, motion, or question
Parliamentary Context: Explicit rejection or disagreement with the matter
Statistical Weight: +1 in opposition calculations

Usage Patterns: - Opposition to proposed legislation - Rejection of amendments - Disagreement with committee conclusions - Opposition to procedural decisions

3. Fravær (Absence)

ID: 3
Danish: Fravær
English: Absence/Not Present
Meaning: Member was not present during the voting session
Parliamentary Context: Physical absence from the voting process
Statistical Weight: Excluded from voting calculations

Important Distinctions: - Involuntary non-participation - Member could not vote due to absence - Not counted in quorum for vote validity calculations - Different from abstention - No deliberate choice was made - Common causes: Illness, travel, scheduling conflicts, other parliamentary duties

Analysis Considerations: - High absence rates may indicate scheduling issues or strategic behavior - Absence patterns can reveal member priorities and engagement levels - Should be excluded from voting alignment analysis - Important for quorum and participation rate calculations

4. Hverken for eller imod (Neither For Nor Against)

ID: 4
Danish: Hverken for eller imod
English: Neither For Nor Against/Abstain/Neutral
Meaning: Member deliberately chose not to support either side
Parliamentary Context: Conscious decision to remain neutral on the matter
Statistical Weight: +1 in abstention calculations, excluded from For/Against ratios

Abstention Motivations: - Conflict of interest - Personal or financial interests in the outcome - Insufficient information - Need for more data before deciding - Partial agreement - Support for some aspects but not others - Strategic positioning - Political calculation for future negotiations - Conscience clause - Personal moral or ethical reservations - Party discipline conflicts - Personal disagreement with party position

Parliamentary Procedures: - Counted as present for quorum purposes - Recorded as deliberate choice in parliamentary records - May be accompanied by explanatory statements - Distinct parliamentary significance from absence

Voting Session Types and Classification

Standard Legislative Votes

The four vote types apply universally across all parliamentary voting procedures:

First Reading Votes: - Initial consideration of proposed legislation - Full vote type spectrum typically utilized - High strategic value for party positioning

Second Reading Votes: - Detailed consideration after committee review - Amendment votes may show complex patterns - Abstentions often indicate negotiation dynamics

Third Reading Votes: - Final passage votes on legislation - Typically highest participation rates - Clearest For/Against divisions

Amendment and Procedural Votes

Vote type usage patterns differ for procedural matters:

Amendment Votes: - More frequent abstentions due to complexity - Tactical voting behaviors more common - Coalition dynamics clearly visible

Procedural Motions: - Often unanimous or near-unanimous - Absence patterns may indicate informal agreement - Quick voting procedures with limited debate

Vote Aggregation and Statistical Analysis

Basic Vote Statistics

# Calculate basic voting statistics for a specific voting session
import requests

def analyze_voting_session(afstemning_id):
    """Analyze vote distribution for a specific voting session."""

    # Fetch all votes for the session
    response = requests.get(
        f"https://oda.ft.dk/api/Stemme?%24filter=afstemningid%20eq%20{afstemning_id}&%24expand=Stemmetype"
    )
    votes = response.json()['value']

    # Count votes by type
    vote_counts = {
        'For': 0,
        'Imod': 0, 
        'Fravær': 0,
        'Hverken for eller imod': 0
    }

    for vote in votes:
        vote_type = vote['Stemmetype']['type']
        vote_counts[vote_type] += 1

    # Calculate percentages and participation
    total_votes = sum(vote_counts.values())
    participating_votes = total_votes - vote_counts['Fravær']

    results = {
        'total_members': total_votes,
        'participation_rate': (participating_votes / total_votes) * 100,
        'for_percentage': (vote_counts['For'] / participating_votes) * 100 if participating_votes > 0 else 0,
        'against_percentage': (vote_counts['Imod'] / participating_votes) * 100 if participating_votes > 0 else 0,
        'abstention_rate': (vote_counts['Hverken for eller imod'] / total_votes) * 100,
        'absence_rate': (vote_counts['Fravær'] / total_votes) * 100,
        'vote_counts': vote_counts
    }

    return results

# Example usage
session_stats = analyze_voting_session(4249)
print(f"Participation Rate: {session_stats['participation_rate']:.1f}%")
print(f"Support Level: {session_stats['for_percentage']:.1f}%") 
print(f"Opposition Level: {session_stats['against_percentage']:.1f}%")

Advanced Voting Pattern Analysis

def analyze_member_voting_patterns(aktor_id, period_id=None):
    """Analyze voting patterns for a specific parliamentary member."""

    # Build query with optional period filter
    query = f"https://oda.ft.dk/api/Stemme?%24filter=aktørid%20eq%20{aktor_id}"
    if period_id:
        query += f"%20and%20Afstemning/periodeid%20eq%20{period_id}"
    query += "&%24expand=Stemmetype,Afstemning(%24expand=Sag)"

    response = requests.get(query)
    votes = response.json()['value']

    # Analyze voting behavior
    patterns = {
        'total_votes': len(votes),
        'vote_distribution': {},
        'participation_rate': 0,
        'alignment_scores': {},
        'case_types': {}
    }

    # Count vote types
    for vote in votes:
        vote_type = vote['Stemmetype']['type']
        patterns['vote_distribution'][vote_type] = patterns['vote_distribution'].get(vote_type, 0) + 1

        # Analyze by case type
        if 'Afstemning' in vote and 'Sag' in vote['Afstemning']:
            case_type = vote['Afstemning']['Sag']['typeid']
            if case_type not in patterns['case_types']:
                patterns['case_types'][case_type] = {'For': 0, 'Imod': 0, 'Abstain': 0, 'Absent': 0}

            if vote_type == 'For':
                patterns['case_types'][case_type]['For'] += 1
            elif vote_type == 'Imod':
                patterns['case_types'][case_type]['Against'] += 1
            elif vote_type == 'Hverken for eller imod':
                patterns['case_types'][case_type]['Abstain'] += 1
            else:
                patterns['case_types'][case_type]['Absent'] += 1

    # Calculate participation rate
    absent_votes = patterns['vote_distribution'].get('Fravær', 0)
    patterns['participation_rate'] = ((patterns['total_votes'] - absent_votes) / patterns['total_votes']) * 100

    return patterns

# Example: Analyze voting patterns for a specific member
member_patterns = analyze_member_voting_patterns(5, period_id=20)
print(f"Member Participation Rate: {member_patterns['participation_rate']:.1f}%")
print("Vote Distribution:", member_patterns['vote_distribution'])

Historical Voting Behavior Analysis

def analyze_voting_trends_over_time(start_date, end_date):
    """Analyze how voting behavior patterns change over time."""

    query = f"""https://oda.ft.dk/api/Afstemning?%24filter=dato%20ge%20datetime'{start_date}'%20and%20dato%20le%20datetime'{end_date}'&%24expand=Stemme(%24expand=Stemmetype)&%24orderby=dato"""

    response = requests.get(query)
    sessions = response.json()['value']

    monthly_trends = {}

    for session in sessions:
        date = session['dato'][:7]  # YYYY-MM format

        if date not in monthly_trends:
            monthly_trends[date] = {
                'total_sessions': 0,
                'avg_participation': 0,
                'avg_abstention': 0,
                'vote_types': {'For': 0, 'Imod': 0, 'Fravær': 0, 'Hverken for eller imod': 0}
            }

        monthly_trends[date]['total_sessions'] += 1

        # Calculate session statistics
        if 'Stemme' in session:
            votes = session['Stemme']
            total_votes = len(votes)

            session_counts = {'For': 0, 'Imod': 0, 'Fravær': 0, 'Hverken for eller imod': 0}
            for vote in votes:
                vote_type = vote['Stemmetype']['type']
                session_counts[vote_type] += 1
                monthly_trends[date]['vote_types'][vote_type] += 1

            # Update averages
            absent = session_counts['Fravær']
            abstain = session_counts['Hverken for eller imod']

            participation = ((total_votes - absent) / total_votes) * 100 if total_votes > 0 else 0
            abstention = (abstain / total_votes) * 100 if total_votes > 0 else 0

            # Running average calculation
            sessions_count = monthly_trends[date]['total_sessions']
            monthly_trends[date]['avg_participation'] = (
                (monthly_trends[date]['avg_participation'] * (sessions_count - 1) + participation) / sessions_count
            )
            monthly_trends[date]['avg_abstention'] = (
                (monthly_trends[date]['avg_abstention'] * (sessions_count - 1) + abstention) / sessions_count
            )

    return monthly_trends

# Example: Analyze trends from 2020 to present
trends = analyze_voting_trends_over_time('2020-01-01T00:00:00', '2024-12-31T23:59:59')

Party Voting Cohesion Analysis

def analyze_party_voting_cohesion(party_id, afstemning_id):
    """Calculate party voting cohesion for a specific vote."""

    # Get all party members who voted
    query = f"""https://oda.ft.dk/api/Stemme?%24filter=afstemningid%20eq%20{afstemning_id}&%24expand=Aktør(%24filter=typeid%20eq%205%20and%20Partier/any(p:%20p/id%20eq%20{party_id})),Stemmetype"""

    response = requests.get(query)
    votes = response.json()['value']

    # Filter to only party member votes
    party_votes = [v for v in votes if 'Aktør' in v and v['Aktør']]

    if not party_votes:
        return {'cohesion_score': 0, 'vote_distribution': {}, 'unity_type': 'no_data'}

    # Count vote types (excluding absences from cohesion calculation)
    vote_counts = {'For': 0, 'Imod': 0, 'Hverken for eller imod': 0}
    total_participating = 0

    for vote in party_votes:
        vote_type = vote['Stemmetype']['type']
        if vote_type != 'Fravær':
            vote_counts[vote_type] += 1
            total_participating += 1

    if total_participating == 0:
        return {'cohesion_score': 0, 'vote_distribution': vote_counts, 'unity_type': 'all_absent'}

    # Calculate cohesion score
    max_agreement = max(vote_counts.values())
    cohesion_score = (max_agreement / total_participating) * 100

    # Determine unity type
    if cohesion_score == 100:
        unity_type = 'unanimous'
    elif cohesion_score >= 90:
        unity_type = 'high_cohesion'
    elif cohesion_score >= 75:
        unity_type = 'moderate_cohesion'
    elif cohesion_score >= 50:
        unity_type = 'low_cohesion'
    else:
        unity_type = 'divided'

    return {
        'cohesion_score': cohesion_score,
        'vote_distribution': vote_counts,
        'unity_type': unity_type,
        'participating_members': total_participating,
        'total_members': len(party_votes)
    }

API Querying Strategies

Basic Vote Type Queries

# Get all vote types
curl "https://oda.ft.dk/api/Stemmetype"

# Get specific vote type details
curl "https://oda.ft.dk/api/Stemmetype(1)"

# Get all votes of a specific type
curl "https://oda.ft.dk/api/Stemme?%24filter=typeid%20eq%201&%24top=100"

Advanced Filtering by Vote Type

# Get all "For" votes in recent voting sessions
curl "https://oda.ft.dk/api/Stemme?%24filter=typeid%20eq%201%20and%20Afstemning/dato%20ge%20datetime'2024-01-01'&%24expand=Afstemning,Aktør&%24top=1000"

# Find all abstentions (Hverken for eller imod) for a specific case
curl "https://oda.ft.dk/api/Stemme?%24filter=typeid%20eq%204%20and%20Afstemning/sagid%20eq%201234&%24expand=Aktør,Stemmetype"

# Analyze absence patterns for a specific period
curl "https://oda.ft.dk/api/Stemme?%24filter=typeid%20eq%203%20and%20Afstemning/periodeid%20eq%2020&%24expand=Aktør&%24top=5000"

Complex Vote Analysis Queries

# Compare voting patterns between parties
curl "https://oda.ft.dk/api/Stemme?%24filter=afstemningid%20eq%204249&%24expand=Aktør(%24expand=Partier),Stemmetype&%24top=200"

# Find cases with high abstention rates
curl "https://oda.ft.dk/api/Afstemning?%24expand=Stemme(%24expand=Stemmetype)&%24filter=Stemme/any(s:%20s/typeid%20eq%204)&%24top=100"

# Analyze individual member voting consistency
curl "https://oda.ft.dk/api/Stemme?%24filter=aktørid%20eq%205&%24expand=Stemmetype,Afstemning(%24expand=Sag)&%24orderby=Afstemning/dato%20desc&%24top=500"

Voting Sessions (Afstemning)

Each vote is linked to a specific voting session through afstemningid:

# Get vote type distribution for a specific voting session
def get_session_vote_breakdown(afstemning_id):
    query = f"https://oda.ft.dk/api/Afstemning({afstemning_id})?%24expand=Stemme(%24expand=Stemmetype)"
    response = requests.get(query)
    session = response.json()

    breakdown = {}
    for vote in session.get('Stemme', []):
        vote_type = vote['Stemmetype']['type']
        breakdown[vote_type] = breakdown.get(vote_type, 0) + 1

    return breakdown

Parliamentary Actors (Aktør)

Vote types reveal individual and institutional voting behavior:

# Analyze an actor's voting type preferences
def actor_vote_preferences(aktor_id, limit=1000):
    query = f"https://oda.ft.dk/api/Stemme?%24filter=aktørid%20eq%20{aktor_id}&%24expand=Stemmetype&%24top={limit}"
    response = requests.get(query)
    votes = response.json()['value']

    preferences = {}
    for vote in votes:
        vote_type = vote['Stemmetype']['type']
        preferences[vote_type] = preferences.get(vote_type, 0) + 1

    return preferences

Cases and Legislation (Sag)

Vote type patterns reveal case complexity and controversy:

# Analyze vote type patterns by case type
def case_voting_patterns(sag_id):
    query = f"https://oda.ft.dk/api/Afstemning?%24filter=sagid%20eq%20{sag_id}&%24expand=Stemme(%24expand=Stemmetype)"
    response = requests.get(query)
    sessions = response.json()['value']

    case_patterns = {
        'total_sessions': len(sessions),
        'vote_evolution': [],
        'overall_distribution': {'For': 0, 'Imod': 0, 'Fravær': 0, 'Hverken for eller imod': 0}
    }

    for session in sessions:
        session_breakdown = {}
        for vote in session.get('Stemme', []):
            vote_type = vote['Stemmetype']['type']
            session_breakdown[vote_type] = session_breakdown.get(vote_type, 0) + 1
            case_patterns['overall_distribution'][vote_type] += 1

        case_patterns['vote_evolution'].append({
            'date': session['dato'],
            'votes': session_breakdown
        })

    return case_patterns

Vote Type Validation and Data Quality

Data Integrity Checks

def validate_vote_type_data():
    """Perform comprehensive vote type data validation."""

    # Check vote type enumeration completeness
    response = requests.get("https://oda.ft.dk/api/Stemmetype")
    vote_types = response.json()['value']

    validation_results = {
        'vote_type_count': len(vote_types),
        'expected_types': ['For', 'Imod', 'Fravær', 'Hverken for eller imod'],
        'missing_types': [],
        'unexpected_types': [],
        'id_consistency': True,
        'orphaned_votes': 0
    }

    # Validate expected vote types exist
    actual_types = [vt['type'] for vt in vote_types]
    for expected in validation_results['expected_types']:
        if expected not in actual_types:
            validation_results['missing_types'].append(expected)

    for actual in actual_types:
        if actual not in validation_results['expected_types']:
            validation_results['unexpected_types'].append(actual)

    # Check for consistent ID mapping
    expected_ids = {1: 'For', 2: 'Imod', 3: 'Fravær', 4: 'Hverken for eller imod'}
    for vote_type in vote_types:
        expected_type = expected_ids.get(vote_type['id'])
        if expected_type != vote_type['type']:
            validation_results['id_consistency'] = False

    # Check for orphaned votes (votes with invalid type IDs)
    response = requests.get("https://oda.ft.dk/api/Stemme?%24filter=typeid%20gt%204%20or%20typeid%20lt%201&%24top=100")
    orphaned = response.json()['value']
    validation_results['orphaned_votes'] = len(orphaned)

    return validation_results

Quality Metrics and Monitoring

def calculate_vote_quality_metrics(period_id=None):
    """Calculate data quality metrics for vote type data."""

    query = "https://oda.ft.dk/api/Stemme?%24expand=Stemmetype,Afstemning"
    if period_id:
        query += f"&%24filter=Afstemning/periodeid%20eq%20{period_id}"
    query += "&%24top=10000"

    response = requests.get(query)
    votes = response.json()['value']

    metrics = {
        'total_votes': len(votes),
        'valid_type_assignments': 0,
        'missing_type_data': 0,
        'participation_rates': [],
        'abstention_rates': [],
        'data_completeness': 0
    }

    session_stats = {}

    for vote in votes:
        # Check vote type data validity
        if 'Stemmetype' in vote and vote['Stemmetype']:
            metrics['valid_type_assignments'] += 1
        else:
            metrics['missing_type_data'] += 1

        # Group by voting session for participation analysis
        session_id = vote['afstemningid']
        if session_id not in session_stats:
            session_stats[session_id] = {'total': 0, 'absent': 0, 'abstain': 0}

        session_stats[session_id]['total'] += 1

        if vote['Stemmetype']['type'] == 'Fravær':
            session_stats[session_id]['absent'] += 1
        elif vote['Stemmetype']['type'] == 'Hverken for eller imod':
            session_stats[session_id]['abstain'] += 1

    # Calculate session-level metrics
    for session in session_stats.values():
        participation_rate = ((session['total'] - session['absent']) / session['total']) * 100
        abstention_rate = (session['abstain'] / session['total']) * 100

        metrics['participation_rates'].append(participation_rate)
        metrics['abstention_rates'].append(abstention_rate)

    # Calculate overall data completeness
    metrics['data_completeness'] = (metrics['valid_type_assignments'] / metrics['total_votes']) * 100

    # Calculate statistical summaries
    if metrics['participation_rates']:
        metrics['avg_participation'] = sum(metrics['participation_rates']) / len(metrics['participation_rates'])
        metrics['avg_abstention'] = sum(metrics['abstention_rates']) / len(metrics['abstention_rates'])

    return metrics

Applications in Political Analysis and Research

Legislative Opposition Analysis

def analyze_opposition_voting_patterns(opposition_party_ids, period_id):
    """Analyze how opposition parties use different vote types strategically."""

    results = {}

    for party_id in opposition_party_ids:
        # Get all votes by party members in the period
        query = f"""https://oda.ft.dk/api/Stemme?%24filter=Afstemning/periodeid%20eq%20{period_id}&%24expand=Aktør(%24filter=Partier/any(p:%20p/id%20eq%20{party_id})),Stemmetype,Afstemning(%24expand=Sag)&%24top=5000"""

        response = requests.get(query)
        votes = [v for v in response.json()['value'] if v.get('Aktør')]

        # Analyze strategic voting patterns
        patterns = {
            'total_votes': len(votes),
            'strategic_abstentions': 0,  # Abstentions on government proposals
            'protest_absences': 0,       # High absence rates on controversial votes
            'unified_opposition': 0,     # Cases where opposition voted together
            'vote_distribution': {'For': 0, 'Imod': 0, 'Fravær': 0, 'Hverken for eller imod': 0}
        }

        for vote in votes:
            vote_type = vote['Stemmetype']['type']
            patterns['vote_distribution'][vote_type] += 1

            # Identify strategic patterns
            if vote_type == 'Hverken for eller imod':
                # Check if this was on a government proposal
                case = vote.get('Afstemning', {}).get('Sag', {})
                if case.get('typeid') in [3, 5]:  # Government bill types
                    patterns['strategic_abstentions'] += 1

        results[party_id] = patterns

    return results

Voting Behavior Research Applications

def research_voting_consistency(research_question="party_discipline"):
    """Generate datasets for political science research questions."""

    if research_question == "party_discipline":
        # Measure party voting discipline using vote type consistency
        return analyze_party_discipline_by_vote_type()

    elif research_question == "issue_salience":
        # Use abstention rates to measure issue salience and controversy
        return analyze_issue_salience_via_abstentions()

    elif research_question == "coalition_stability":
        # Track coalition voting patterns through vote type evolution
        return analyze_coalition_stability_patterns()

def analyze_party_discipline_by_vote_type():
    """Measure party discipline using vote type distribution analysis."""

    # Get recent legislative votes with party information
    query = """https://oda.ft.dk/api/Afstemning?%24filter=dato%20ge%20datetime'2023-01-01'%20and%20Sag/typeid%20eq%203&%24expand=Stemme(%24expand=Aktør(%24expand=Partier),Stemmetype),Sag&%24top=100"""

    response = requests.get(query)
    sessions = response.json()['value']

    discipline_scores = {}

    for session in sessions:
        votes = session.get('Stemme', [])
        party_votes = {}

        # Group votes by party
        for vote in votes:
            actor = vote.get('Aktør')
            if not actor or not actor.get('Partier'):
                continue

            party_id = actor['Partier'][0]['id']  # Primary party
            vote_type = vote['Stemmetype']['type']

            if party_id not in party_votes:
                party_votes[party_id] = []
            party_votes[party_id].append(vote_type)

        # Calculate discipline for each party
        for party_id, votes in party_votes.items():
            if len(votes) < 3:  # Minimum sample size
                continue

            # Exclude absences from discipline calculation
            active_votes = [v for v in votes if v != 'Fravær']

            if len(active_votes) == 0:
                continue

            # Calculate unity (most common vote type percentage)
            vote_counts = {}
            for vote in active_votes:
                vote_counts[vote] = vote_counts.get(vote, 0) + 1

            max_agreement = max(vote_counts.values())
            discipline_score = (max_agreement / len(active_votes)) * 100

            if party_id not in discipline_scores:
                discipline_scores[party_id] = []
            discipline_scores[party_id].append(discipline_score)

    # Calculate average discipline scores
    avg_discipline = {}
    for party_id, scores in discipline_scores.items():
        avg_discipline[party_id] = sum(scores) / len(scores)

    return avg_discipline

Democratic Participation Analysis

def analyze_democratic_participation_metrics():
    """Comprehensive analysis of democratic participation using vote types."""

    # Get recent voting data
    query = """https://oda.ft.dk/api/Afstemning?%24filter=dato%20ge%20datetime'2023-01-01'&%24expand=Stemme(%24expand=Stemmetype)&%24top=500"""

    response = requests.get(query)
    sessions = response.json()['value']

    participation_metrics = {
        'total_sessions': len(sessions),
        'avg_participation_rate': 0,
        'engagement_quality': {},
        'democratic_responsiveness': {},
        'participation_trends': []
    }

    session_participation = []

    for session in sessions:
        votes = session.get('Stemme', [])
        if not votes:
            continue

        # Calculate session participation metrics
        total_eligible = len(votes)
        absent = len([v for v in votes if v['Stemmetype']['type'] == 'Fravær'])
        abstaining = len([v for v in votes if v['Stemmetype']['type'] == 'Hverken for eller imod'])
        active_voters = total_eligible - absent - abstaining

        participation_rate = ((total_eligible - absent) / total_eligible) * 100
        engagement_rate = (active_voters / total_eligible) * 100

        session_participation.append(participation_rate)

        participation_metrics['participation_trends'].append({
            'date': session['dato'],
            'participation_rate': participation_rate,
            'engagement_rate': engagement_rate,
            'abstention_rate': (abstaining / total_eligible) * 100
        })

    # Calculate overall metrics
    if session_participation:
        participation_metrics['avg_participation_rate'] = sum(session_participation) / len(session_participation)

    return participation_metrics

Advanced Research Applications

Temporal Voting Evolution Analysis

def analyze_temporal_voting_evolution(start_year, end_year):
    """Track how voting behavior patterns evolve over time using vote types."""

    yearly_patterns = {}

    for year in range(start_year, end_year + 1):
        query = f"""https://oda.ft.dk/api/Afstemning?%24filter=year(dato)%20eq%20{year}&%24expand=Stemme(%24expand=Stemmetype)&%24top=1000"""

        response = requests.get(query)
        sessions = response.json()['value']

        year_stats = {
            'total_sessions': len(sessions),
            'vote_type_evolution': {'For': 0, 'Imod': 0, 'Fravær': 0, 'Hverken for eller imod': 0},
            'polarization_index': 0,
            'engagement_score': 0
        }

        total_votes = 0
        for session in sessions:
            for vote in session.get('Stemme', []):
                vote_type = vote['Stemmetype']['type']
                year_stats['vote_type_evolution'][vote_type] += 1
                total_votes += 1

        if total_votes > 0:
            # Calculate polarization (For + Against vs Abstentions + Absences)
            decisive_votes = year_stats['vote_type_evolution']['For'] + year_stats['vote_type_evolution']['Imod']
            non_decisive = year_stats['vote_type_evolution']['Fravær'] + year_stats['vote_type_evolution']['Hverken for eller imod']

            year_stats['polarization_index'] = (decisive_votes / total_votes) * 100
            year_stats['engagement_score'] = ((total_votes - year_stats['vote_type_evolution']['Fravær']) / total_votes) * 100

            # Convert to percentages
            for vote_type in year_stats['vote_type_evolution']:
                year_stats['vote_type_evolution'][vote_type] = (year_stats['vote_type_evolution'][vote_type] / total_votes) * 100

        yearly_patterns[year] = year_stats

    return yearly_patterns

This comprehensive vote type classification system provides the foundation for sophisticated parliamentary analysis, enabling researchers and developers to build powerful tools for understanding democratic processes, party dynamics, and individual legislator behavior in the Danish Parliament.