Εργαλεια & Πρακτικες

How to: Πώς δημιουργούμε αυτόματα δεκάδες γραφήματα μέσω του Datawrapper API

Δες το άρθρο

Δημιουργήσαμε δεκάδες γραφήματα τα οποία ανανεώνονται αυτόματα καθημερινά χωρίς δική μας παρέμβαση χάρη σε μερικές γραμμές κώδικα Python και το Datawrapper API.

Η παραγωγή μεγάλου αριθμού οπτικοποιήσεων σε μικρό χρονικό διάστημα είναι συχνά μια δύσκολη, κουραστική, και πολλές φορές αναπόφευκτη, προσέγγιση στην παρουσίαση των δεδομένων στη δημοσιογραφία.

Για να εμπλουτιστεί περαιτέρω ένα θέμα και να παρουσιαστεί μία πιο σφαιρική οπτική των στοιχείων που απαρτίζουν το ρεπορτάζ μπορεί να χρειαστεί να κάνουμε ένα ξεχωριστό γράφημα ή χάρτη για κάθε μικρή λεπτομέρεια, χρονική, γεωγραφική κλπ. Η διαδικασία περιπλέκεται ακόμα περαιτέρω, εάν πρέπει να επαναλαμβάνεται σε τακτά χρονικά διαστήματα, όταν τα δεδομένα ανανεώνονται.

Ένας τρόπος για να αποφύγουμε μία τόσο χρονοβόρα πρακτική είναι να αξιοποιήσουμε το API του Datawrapper, με το οποίο μπορούμε να δημιουργήσουμε γραφήματα άμεσα, χρησιμοποιώντας λίγες γραμμές κώδικα Python.

Στο άρθρο του iMEdD για τις τιμές των καυσίμων επιστρατεύσαμε αυτήν την προσέγγιση για να οπτικοποιήσουμε τα δεδομένα των τιμών καυσίμων ανά νομό από την αρχή του 2026. Καθώς η Ελλάδα απαρτίζεται από δεκάδες περιφερειακές ενότητες και η ανάλυση έπρεπε να διεξαχθεί για τέσσερις διαφορετικούς τύπους καυσίμων, η χειρωνακτική αλλαγή των δεδομένων, και μάλιστα σε καθημερινή βάση, δεν αποτελούσε βιώσιμη επιλογή.

Δημιουργία κλειδιού

Το πρώτο βήμα, για να χρησιμοποιήσετε το Datawrapper API, είναι η δημιουργία κλειδιού (key), χωρίς το οποίο δεν θα μπορέσετε να προχωρήσετε περαιτέρω.

Η διαδικασία είναι εύκολη. Πηγαίνετε στο κεντρικό μενού (dashboard) του Datawrapper ενώ είστε συνδεδεμένοι στον λογαριασμό σας, και περάστε το ποντίκι σας πάνω από το όνομα του λογαριασμού, στο πάνω δεξιά τμήμα της οθόνης. Τότε, θα εμφανιστεί ένα dropdown menu το οποίο διαθέτει την επιλογή «Ρυθμίσεις & Λογαριασμός» (Settings & Account). Όταν πατήσετε την επιλογή αυτή θα μεταφερθείτε στο περιβάλλον των επιλογών για τον λογαριασμό σας.

Τώρα, στο πάνω αριστερά τμήμα της οθόνης θα δείτε την επιλογή «API Tokens», την οποία όταν την κλικάρετε θα μεταφερθείτε στο περιβάλλον «API Access Tokens». Εκεί θα δείτε το κουμπί «Δημιουργήστε Access Token». Το access token είναι το κλειδί σας. Αποθηκεύστε το κάπου ασφαλή άμεσα, καθώς μετά την πρώτη εμφάνισή του δεν θα μπορείτε να το ξαναδείτε ολόκληρο.

Γράφημα με λίγες γραμμές κώδικα

Η δημιουργία γραφημάτων με Python δεν χρειάζεται να είναι δύσκολη διαδικασία αν χρησιμοποιήσουμε ένα οποιοδήποτε LLM μοντέλο τεχνητής νοημοσύνης. Για όσους και όσες μάλιστα δεν έχουν κάποιο εργαλείο επεξεργασίας κώδικα εγκατεστημένο στον υπολογιστή, μπορείτε να γράψετε στο Google Colab, εργαλείο της Google το οποίο μπορείτε να τρέξετε από τον περιηγητή (browser) σας.

Τα βήματα για τη δημιουργία ενός απλού διαγράμματος:

  • Ανοίξτε ένα νέο notebook στο Google Colab
  • Εγκαταστήστε τη βιβλιοθήκη του Datawrapper στο runtime σας, τρέχοντας τον κώδικα: !pip install datawrapper
  • Εγκατάσταση άλλων βιβλιοθηκών που χρειάζεστε (import pandas as pd, import os, κλπ.)
  • Πηγή των δεδομένων: αυτή μπορεί να είναι url αλλά και dataframe που φτιάξαμε στο google colab από δικά μας στοιχεία, από αρχείο excel, κλπ.
  • Ορισμός του line chart και των μεταβλητών του με το dw.LineChart()
  • Τελευταίο βήμα η δημιουργία του γραφήματος με το create()

Παράδειγμα για line chart που μπορεί να χρησιμοποιηθεί σε Google Colab notebook:

 
# Import necessary libraries 

import pandas as pd 

import datawrapper as dw 

import os 

 

# Add the token to the environment 

API_KEY = “insert_your_key_here” 

os.environ["DATAWRAPPER_ACCESS_TOKEN"] = API_KEY 

 

# Load sample data about temperatures 

df = pd.DataFrame({ 

    "Year": list(range(2000, 2011)), 

    "LandAverageTemperature": [14.3, 14.5, 14.6, 14.8, 15.0, 15.1, 15.3, 15.4, 15.6, 15.7, 15.9] 

}) 

 

chart = dw.LineChart( 

    # Chart title 

    title="Global land temperature in July, 2000-2011", 

    # Data source attribution 

    source_name="Berkeley Earth", 

    source_url="http://berkeleyearth.org/data/", 

    # Get the data from the dataframe 

    data=df, 

    # Set the suffix on our values 

    transformations=dw.Transform( 

        column_format=[ 

            dw.ColumnFormat( 

                column="LandAverageTemperature", 

                number_append=" °C", 

            ) 

        ] 

    ), 

    # Set the Y-axis range 

    custom_range_y=[8, 21], 

    # Format Y-axis grid labels with no decimal places 

    y_grid_format="0", 

    # Tooltip format 

    tooltip_number_format="00.00", 

    tooltip_x_format="YYYY", 

    # Configure the main temperature line's color 

    color_category={ 

        "LandAverageTemperature": "#1d81a2", 

    }, 

    lines=[ 

        # Style the main line 

        dw.Line( 

            column="LandAverageTemperature", 

            width=dw.LineWidth.THIN, 

            interpolation=dw.LineInterpolation.CURVED, 

        ) 

    ] 

) 

# Last step. Create the chart 

chart.create() 

Η δημιουργία του γραφήματος γίνεται με dw.LineChart(), το οποίο συγκεντρώνει όλες τις μεταβλητές που επιθυμούμε να διαθέτει το γράφημά μας σε ένα variable (στην προκειμένη περίπτωση το “chart”. Εδώ τοποθετούνται οι μεταβλητές που θέλουμε να ορίσουμε για το γράφημα, όπως ο τίτλος και η πηγή των δεδομένων, αλλά και το στυλ της γραμμής, το περιεχόμενο του tooltip, κ.ά.

Η τελευταία κίνηση που απομένει, όταν έχουμε ορίσει, τις μεταβλητές στο chart είναι να γράψουμε την εντολή .create().

Εικόνα διαγράμματος που έχει δημιουργηθεί με Datawrapper API και εμφανίζεται με “hover” στον νομό Ατττικής στο παραπάνω χάρτη. Στην εικόνα, αποτυπώνεται η εξέλιξη της τιμής της Αμόλυβδης 95 στον νομό Αττικής από την αρχή του 2026.

Πώς φτιάξαμε εικόνες που ανανεώνονται από δεκάδες γραφήματα

Στην περίπτωση των γραφημάτων που θέλαμε να δημιουργήσουμε για την οπτικοποίηση των παλαιότερων τιμών καυσίμων ανά περιοχή, χρησιμοποιήσαμε το Github και την γλώσσα προγραμματισμού Python για να έχουμε μία αυτοματοποιημένη διαδικασία που θα επαναλαμβάνεται καθημερινώς.

Έχοντας ως πηγή δεδομένων αρχείο .csv με τις τιμές καυσίμων ανά νομό, δημιουργήσαμε γραφήματα για κάθε περιοχή και κάθε τύπο καυσίμου. Κάθε γράφημα που δημιουργήθηκε διαθέτει έναν ξεχωριστό κωδικό (“id”) με τον οποίο μπορεί να ταυτοποιηθεί εύκολα. Καθώς στη δική μας περίπτωση ο σκοπός ήταν να ανανεώνονται τα στοιχεία, ο κώδικας χωρίστηκε σε δύο μέρη:

  • α) την αρχική δημιουργία των γραφημάτων και
  • β) την ανανέωσή τους.

Αρχική δημιουργία των γραφημάτων

Για το πρώτο μέρος του πρότζεκτ, φτιάξαμε ένα πρόγραμμα το οποίο δημιουργεί γραφήματα για κάθε περιοχή που βρίσκεται στη στήλη «prefecture» του αρχείου .csv, καθώς και για κάθε τύπο καυσίμου. Από εκεί προχωράμε και ταυτίζουμε τον κάθε τύπο καυσίμων με συγκεκριμένο χρωματικό κώδικα. Τα id των γραφημάτων, καθώς και το όνομα της περιοχής και ο τύπους καυσίμου αποθηκεύονται σε αρχείο .json, για να χρησιμοποιηθούν από επόμενο script, για το δεύτερο μέρος του πρότζεκτ.

Μέσα στο dw.LineChart() έχουμε τοποθετήσει διάφορες μεταβλητές για να εκπληρώσουμε τις διάφορες απαιτήσεις του συγκεκριμένου πρότζεκτ μας.

Στη δική μας περίπτωση, για παράδειγμα, δεν δώσαμε τίτλο, και για αυτό λείπει η παράμετρος «title=». Ταυτόχρονα επιλέξαμε να φαίνεται μόνο η ημερομηνία της 28ης Φεβρουαρίου στον άξονα Χ (custom_ticks_x=[“2026-02-28”]), καθώς θέλουμε να διαχωρίζεται οπτικά η εξέλιξη των τιμών πριν και μετά την έναρξη της σύρραξης στη Μέση Ανατολή.

Τμήμα του script για τη δημιουργία πολλαπλών γραφημάτων

 
# Data source URL
CSV_URL = "https://raw.githubusercontent.com/iMEdD-Lab/fuel_prices_data/refs/heads/main/prefectures/update_master/master_pref_upd.csv"

# Map each fuel type to a color
FUEL_COLORS = {
    "Αμόλυβδη 95": "#08A021",
    "Αμόλυβδη 100": "#FFCB6F",
    "Diesel Κίνησης": "#15607a",
    "Autogas": "#F54927"
}

CHART_CONFIG_FILE = "ids/chart_config.json"
Path(CHART_CONFIG_FILE).parent.mkdir(parents=True, exist_ok=True)


# LOAD CSV
df = pd.read_csv(CSV_URL)
df['date'] = pd.to_datetime(df['date'], errors='coerce')
df = df[df['date'] >= '2026-01-01']

# Ensure all fuel columns are numeric
for fuel_type in FUEL_COLORS.keys():
    if fuel_type in df.columns:
        df[fuel_type] = pd.to_numeric(df[fuel_type], errors='coerce')


# LOAD EXISTING CONFIG JSON IF EXISTS
config_list = []
if Path(CHART_CONFIG_FILE).exists():
    with open(CHART_CONFIG_FILE, "r", encoding="utf-8") as f:
        config_list = json.load(f)

# LOOP THROUGH PREFECTURES AND FUEL TYPES
prefectures = df['prefecture'].dropna().unique()

for pref in prefectures:
    df_pref = df[df['prefecture'] == pref]

    for fuel_type, color in FUEL_COLORS.items():
        if fuel_type not in df_pref.columns:
            continue

        df_chart = df_pref[['date', fuel_type]].dropna()
        if df_chart.empty:
            print(f"No data for {fuel_type} in {pref}, skipping...")
            continue

        # Compute dynamic lower Y-axis for this fuel type in this prefecture
        min_val = df_chart[fuel_type].min()
        lower_y = math.floor(min_val / 0.15) * 0.15  # round down to nearest 0.15

        
        # CREATE LINE CHART
       
        chart = dw.LineChart(
            source_name="Υπουργείο Ανάπτυξης",
            source_url="https://github.com/pmethodios/fuel_prices",
            data=df_chart,
            x_column="date",
            y_grid_format="0.00",
            custom_range_y=[lower_y, None],
            custom_ticks_y=[1.0, 1.25, 1.5, 1.75, 2.0],  
            tooltip_x_format="DD-MM-YYYY",
            show_tooltips=True,
            dark_mode_invert=True,
            label_colors=True,
            color_category={fuel_type: color},
            x_grid="off",
            x_grid_format="DD/MM",
            custom_ticks_x=["2026-02-28"]
        )

       
        # ADD WIDE LINE
        chart.lines.append(
            dw.Line(
                column=fuel_type,
                width=dw.LineWidth.THICK,
                interpolation=dw.LineInterpolation.CURVED,
                show_first_value_label=True,
                show_last_value_label=True,
                show_symbols=True,
                line_symbol="circle"
            )
        )

    
        # CREATE CHART ON DATAWRAPPER
        result = chart.create()
        chart_id = result.chart_id

  
        # SAVE JSON CONFIG
        config_list.append({
            "chart_id": chart_id,
            "prefecture": pref,
            "fuel_type": fuel_type
        })


# SAVE UPDATED CONFIG LIST
with open(CHART_CONFIG_FILE, "w", encoding="utf-8") as f:
    json.dump(config_list, f, ensure_ascii=False, indent=2)

Ανανέωση των γραφημάτων

Σε άλλο πρόγραμμα, έχουμε τοποθετήσει τις εντολές που καθιστούν το πρότζεκτ μας «ζωντανό», ανανεώνοντας τα δεδομένα σε σχεδόν καθημερινή βάση.

Ξεκινήσαμε με την ίδια προσέγγιση, τον ορισμό του κλειδιού για το API, αλλά σε αυτό το βήμα έχουμε ως επιπρόσθετη πηγή δεδομένων το αρχείο .json με τα ids των γραφημάτων που θέλουμε να ανανεώσουμε.

Μέσω «for loop» το οποίο περνάει από κάθε id και φιλτράρει τα δεδομένα σύμφωνα με το όνομα της περιοχής και τον τύπο καυσίμων, ανανεώνονται τα στοιχεία για όλα τα γραφήματα.

 
# Load the data from the .csv url
df = pd.read_csv(CSV_URL)
df['date'] = pd.to_datetime(df['date'], errors='coerce')

# Ensure numeric fuel columns
for fuel_type in FUEL_COLORS.keys():
    if fuel_type in df.columns:
        df[fuel_type] = pd.to_numeric(df[fuel_type], errors='coerce')


# UPDATE EACH CHART
for chart_info in chart_mappings:
    chart_id = chart_info["chart_id"]
    prefecture = chart_info["prefecture"]
    fuel_type = chart_info["fuel_type"]

    # Filter CSV for this prefecture and fuel type, from Jan 1, 2026
    df_chart = df[(df["prefecture"] == prefecture) & (df["date"] >= "2026-01-01")]
    df_chart = df_chart[["date", fuel_type]].dropna()

    # Skip if no data
    if df_chart.empty:
        print(f"No data for {prefecture} - {fuel_type}, skipping...")
        continue

   
    # ADD PREFECTURE AS A COLUMN
    df_chart['prefecture'] = prefecture


    # CALCULATE PER-FUEL MIN VALUE FOR Y-AXIS
    min_val = df_chart[fuel_type].min()

    # Second closest lower tenth
    lower_y = math.floor(min_val * 10) / 10 - 0.1
    lower_y = max(lower_y, 0)
    

    # ASSIGN COLOR
    color_category = {fuel_type: FUEL_COLORS.get(fuel_type, "#000000")}  # default black if missing


    # UPDATE CHART
    chart = dw.LineChart(
        chart_id=chart_id,
        data=df_chart,
        x_column="date",
        color_category=color_category,
        y_grid_format="0.00",
        custom_range_y=[lower_y, None],
        tooltip_x_format="DD-MM-YYYY",
        show_tooltips=True
    )

    chart.lines.clear()
    chart.lines.append(
        dw.Line(
            column=fuel_type,
            width=dw.LineWidth.MEDIUM,
            interpolation=dw.LineInterpolation.CURVED,
            show_first_value_label=True,
            show_last_value_label=True,
            show_symbols=True,
            line_symbol="circle"
        )
    )

    chart.update()

Εικόνες

Έπειτα από την ανανέωση των γραφημάτων ακολουθεί το τελευταίο βήμα, η δημιουργία των εικόνων: προκειμένου να διαθέσουμε τα διαγράμματα με όλα τα στοιχεία τιμών από την αρχή του 2026 και αυτά να εμφανίζονται, με το πέρασμα του ποντικιού πάνω από τις διάφορες περιοχές του χάρτη, χρειαζόμασταν να μετατρέπουμε τα διαγράμματα σε εικόνες (αρχεία .png).

Η προσέγγιση αυτή επιλέχθηκε καθώς στο Datawrapper δεν είναι δυνατή η ενσωμάτωση διαδραστικών γραφημάτων στο tooltip. Έτσι, επιλέχθηκε να δημιουργηθούν αρχεία εικόνων (.png) για κάθε περιοχή και κάθε τύπου καυσίμου.

Η διαδικασία αυτή έρχεται σε πέρας με την εντολή “.export_png()”.

 
    #Export image from chart
    png_data = chart.export_png()
    # Save the data to path
    with open(local_png_path, "wb") as f:
        f.write(png_data)

Στη δική μας περίπτωση, αποθηκεύουμε το σύνολο αυτών των εικόνων στον φάκελο temp_files, και τα αρχεία ονομάζονται σύμφωνα με το όνομα του νομού και τον εκάστοτε τύπο του καυσίμου. Έτσι, κάθε αρχείο εικόνας βρίσκεται σε ένα μοναδικό url, με το οποίο τροφοδοτούμε το tooltip που εμφανίζεται στους χάρτες.

Το συνολικό documentation για τη δημιουργία line chart με το Datawrapper API μπορείτε να το βρείτε εδώ.

Creative Commons license logo