[Python] log-log time series plot with event markers

Discussão em 'Python' iniciado por Stack, Abril 27, 2025.

  1. Stack

    Stack Membro Participativo

    I want to create a log-log plot showing the growth of publications over time (papers/year), similar to scientific figures like Fig. 1a in "A Century of Physics" (Nature Physics 2015).

    Requirements:

    1. Y-axis (number of papers) on a log scale (10²–10⁶) with ticks at each power of 10.
    2. X-axis (year) with decade ticks (e.g., 1960, 1970, 1980...).
    3. Multiple vertical dashed lines indicating important trend changes.
    4. Serif font, light dashed grid, no top/right spines.
    5. Line colors and styles similar to journal figures (e.g., matplotlib, Seaborn).
    6. A panel label (e.g., "Web of Science") at the top left.

    How can I best style my matplotlib plot to match this?

    The following is what I did (in Python):

    #!/usr/bin/env python3

    """
    replicate_figure1a_misinformation.py

    Count the yearly number of WoS publications (total) versus
    the number of misinformation publications (from combined_journals.xlsx)
    from 1960 to 2024, and plot both curves (log‐scale) to reproduce
    “Figure 1 | The Evolution of Physics” analogous to Sinatra et al.
    """

    import os
    import gzip
    import xml.etree.ElementTree as ET
    from lxml import etree

    import pandas as pd
    import matplotlib.pyplot as plt

    # 1) Configuration
    # ──────────────────────────────────────────────────────────
    # Path to your combined misinformation Excel file
    MISINFO_XLSX = 'combined_journals.xlsx'

    # Root directory containing files like 1960.xml.gz … 2024.xml.gz
    WOS_DIR = '/Users/DataSets/Documents/DataSets/WoS/merged_yearly'

    # Years to include in the plot
    START_YEAR = 1960
    END_YEAR = 2024
    YEARS = list(range(START_YEAR, END_YEAR + 1))


    # 2) Load misinformation paper counts by year
    # ──────────────────────────────────────────────────────────
    misinfo_df = pd.read_excel(MISINFO_XLSX, dtype={'UT (Unique WOS ID)': str,
    'Publication Year': int,
    'WoS Categories': str})

    # ensure only years in our window
    misinfo_df = misinfo_df[
    (misinfo_df['Publication Year'] >= START_YEAR) &
    (misinfo_df['Publication Year'] <= END_YEAR)
    ]

    # count unique UT per year
    mis_counts = (
    misinfo_df
    .drop_duplicates(subset='UT (Unique WOS ID)')
    .groupby('Publication Year')
    .size()
    .reindex(YEARS, fill_value=0)
    .to_dict()
    )

    print(f"Loaded misinformation counts for {len(mis_counts)} years")


    # 3) Parse WoS XML to get total counts per year
    # ──────────────────────────────────────────────────────────
    total_counts = {yr: 0 for yr in YEARS}

    for yr in YEARS:
    gz_path = os.path.join(WOS_DIR, f'{yr}.xml.gz')
    if not os.path.exists(gz_path):
    continue

    with gzip.open(gz_path, 'rb') as f:
    # directly enable recovery and huge_tree here
    for _, rec in etree.iterparse(
    f,
    events=('end',),
    tag='REC',
    recover=True,
    huge_tree=True
    ):
    total_counts[yr] += 1
    rec.clear()


    # 4) Build DataFrame and plot
    # ──────────────────────────────────────────────────────────
    df_counts = pd.DataFrame({
    'Year': YEARS,
    'WoS total': [total_counts[yr] for yr in YEARS],
    'Misinformation': [mis_counts.get(yr, 0) for yr in YEARS],
    })
    df_counts.set_index('Year', inplace=True)

    plt.figure(figsize=(10, 6))
    # plt.plot(df_counts.index, df_counts['WoS total'], label='WoS total papers')
    plt.plot(df_counts.index, df_counts['Misinformation'], label='Web of Science')
    plt.yscale('log')
    plt.xlabel('Year')
    plt.ylabel('Number of papers')
    plt.title('The Evolution of Misinformation (1960–2024)')
    plt.legend()
    plt.grid(True, which='both', linestyle='--', alpha=0.3)
    plt.tight_layout()

    # To save the figure instead of showing:
    plt.savefig('figure1a_misinformation.png', dpi=600)
    # Or to display:
    # plt.show()


    Attached is my plot and the plot of the article I am trying to replicate exactly same.

    [​IMG] [​IMG]

    Continue reading...

Compartilhe esta Página