Spiel und Spaß mit den Benzinpreisen in Deutschland – Teil 2: Grafiken mit Gnuplot!

Nachdem es in Teil 1 drum ging wie wir an die Daten rankommen, probieren wir in Teil 2 mal etwas sinnvolles zu tun – vorallem natürlich Benzingeld zu sparen 🙂

Wir sehen uns also eine bestimmte Tankestelle
(‚7b029caf-172e-4e50-6300-f2991c068432‘)
genauer an und zeichnen den Preisverlauf für E5 Benzin der letzten vier Wochen auf.

Zuerst müssen wir gnuplot installieren:

apt-get install gnuplot

Nun eine Datenbankabfrage die uns den Preisverlauf in den letzten vier vollständigen Kalenderwochen anzeigt:
(-> Wochenanfang ist Montag!)

select
extract(week from date) as "week",
to_char(date, 'ID') || '-' || to_char(date, 'HH24') || ':' || to_char(date, 'MI') as "TagStundeMinute",
e5
from gas_station_information_history
where stid = '7b029caf-172e-4e50-6300-f2991c068432'
and extract(week from date) > extract(week from now()) - 5
and extract(week from date) != extract(week from now())
and extract(year from date) = extract(year from now())
order by date asc;

Wir erhalten hier:
-> die Woche (jede Woche soll eine andersfarbige Linie sein!)
-> Eine Kombination aus Tag der Woche (Montag == 1), Stunde und Minute – diese Punkte zeichnen wir auf unserer x-Achse an
-> Den Preis für E5 Benzin – diese Punkte zeichen wir auf unsere y-Achse an

Leider ist dieses Format schön und gut nur Gnuplot möchte gerne die Wochen als Spalten. Also müssen wir hier durch einige Reifen hüpfen.
Wir erzeugen die Datei ’sql.txt‘ mit folgendem Inhalt:

DO
$$
BEGIN
EXECUTE format('create table benzintemp ("TagStundeMinute" varchar(20), "Woche%s" integer, "Woche%s" integer, "Woche%s" integer, "Woche%s" integer)', 
extract(week from now()) - 4,
extract(week from now()) - 3,
extract(week from now()) - 2,
extract(week from now()) - 1);
END;
$$ LANGUAGE plpgsql;

alter table benzintemp add primary key("TagStundeMinute");

DO
$$
BEGIN
EXECUTE format('insert into benzintemp ("TagStundeMinute", "Woche%s") select to_char(date, ''ID'') || '' '' || to_char(date, ''HH24'') || '' '' || to_char(date, ''MI''), e5 from gas_station_information_history where stid = ''7b029caf-172e-4e50-6300-f2991c068432'' and extract(week from date) = (extract(week from now()) - 4) and extract(year from date) = extract(year from now()) on conflict ("TagStundeMinute") do update set "Woche%s" = excluded."Woche%s"', 
extract(week from now()) - 4, extract(week from now()) - 4, extract(week from now()) - 4);
END;
$$ LANGUAGE plpgsql;

DO
$$
BEGIN
EXECUTE format('insert into benzintemp ("TagStundeMinute", "Woche%s") select to_char(date, ''ID'') || '' '' || to_char(date, ''HH24'') || '' '' || to_char(date, ''MI''), e5 from gas_station_information_history where stid = ''7b029caf-172e-4e50-6300-f2991c068432'' and extract(week from date) = (extract(week from now()) - 3) and extract(year from date) = extract(year from now()) on conflict ("TagStundeMinute") do update set "Woche%s" = excluded."Woche%s"', 
extract(week from now()) - 3, extract(week from now()) - 3, extract(week from now()) - 3);
END;
$$ LANGUAGE plpgsql;

DO
$$
BEGIN
EXECUTE format('insert into benzintemp ("TagStundeMinute", "Woche%s") select to_char(date, ''ID'') || '' '' || to_char(date, ''HH24'') || '' '' || to_char(date, ''MI''), e5 from gas_station_information_history where stid = ''7b029caf-172e-4e50-6300-f2991c068432'' and extract(week from date) = (extract(week from now()) - 2) and extract(year from date) = extract(year from now()) on conflict ("TagStundeMinute") do update set "Woche%s" = excluded."Woche%s"', 
extract(week from now()) - 2, extract(week from now()) - 2, extract(week from now()) - 2);
END;
$$ LANGUAGE plpgsql;

DO
$$
BEGIN
EXECUTE format('insert into benzintemp ("TagStundeMinute", "Woche%s") select to_char(date, ''ID'') || '' '' || to_char(date, ''HH24'') || '' '' || to_char(date, ''MI''), e5 from gas_station_information_history where stid = ''7b029caf-172e-4e50-6300-f2991c068432'' and extract(week from date) = (extract(week from now()) - 1) and extract(year from date) = extract(year from now()) on conflict ("TagStundeMinute") do update set "Woche%s" = excluded."Woche%s"', 
extract(week from now()) - 1, extract(week from now()) - 1, extract(week from now()) - 1);
END;
$$ LANGUAGE plpgsql;

select * from benzintemp order by 1 asc;

drop table benzintemp;

Das ganze packen wir nun in eine Tab getrennte Datei für gnuplot:

psql benzinpreis --quiet --no-align -P "footer=off" --field-separator $'\t' < sql.txt > benzin.dat

Und nun das gnuplot-Script (‚plotme.txt‘) welches die Datei verwursten soll:

set datafile separator "\t"
set timefmt "%d %H %M"
set xdata time
set grid
set title 'Preis Benzin E5 letzte vier Wochen Tankestelle ELO'
set xlabel 'Tag Stunde:Minute'
set ylabel "Preis E5"
set terminal jpeg size 5000,1200
set output 'benzin.jpg'
set key autotitle columnhead
set style data linespoint
set format x "%d %H:%M"
set xtics 10800
plot 'benzin.dat' using 1:2, '' using 1:2:($1) with labels point pt 3 offset char 1,1 notitle, 'benzin.dat' using 1:3, 'benzin.dat' using 1:4, 'benzin.dat' using 1:5

-> Wir zeichnen hier nur für die erste Woche auch die Uhrzeiten mit rein an die Punkte, sonst wird es zu voll

Und los:

gnuplot < plotme.txt

Und was zeigt sich?

- Der Tag beginnt mit dem höchsten Preis um ca. 6 Uhr
- So ca. gegen 9 Uhr kommt schon gleich ein deutlich günstigerer Preis
- Bis kurz vor Mittag geht der Preis meistens runter, der tiefste Preis ist kurz vor 13:30 Uhr
- Um ca. 12:30 Uhr geht der Preis rapide hinauf - aber nicht mehr so hoch wie am morgen
- Danach sinkt der Preis durchgehend bis zum Abend - zumindest am Mo-Do - aber nicht in jeder Woche!
- Am Fr-So gibt es Abends um ca. 17 Uhr nochmal eine relativ hohe Preisspitze - manchmal gibt es diese aber auch von Mo-Do!

Darauf schließen wir, zumindest für diese Tankstelle:

- Auf keinen Fall morgens Tanken!
- Möglichst zwischen Mo-Do abends tanken
- Wenn man zwischen Fr-So tanken muss darauf achten dass es nach 18 Uhr ist um die 17 Uhr Wochenends-Preisspitze nicht mitzunehmen

Zwar ist dieses Wissen jetzt nicht unbedingt neu oder was besonderes, aber jetzt können wir es zumindest definitiv belegen.

Achja, die Grafik gibt es hier!

Und hier gibts noch eine Grafik (diesmal mit Python und Matplotlib gezeichnet) wie der Preisverlauf in der letzten Woche ist: Klick!

7 Antworten auf „Spiel und Spaß mit den Benzinpreisen in Deutschland – Teil 2: Grafiken mit Gnuplot!“

  1. Die Grafik ist verkehrt. Sie geht von 100 Stunden pro Tag aus. Sieht man recht schön bei Woche19 Tag 3. 19:06 müsste bei einer 24-Stunden-Aufteilung der X-Achse viel weiter rechts stehen.

    Korrigieren könnte man das wie folgt: zuerst einmal getrennte Werte für Tag, Stunde und Minute in benzin.dat speichern (Format „Tag Stunde Minute“):

    EXECUTE format(‚insert into benzintemp („TagStundeMinute“, „Woche%s“) select to_char(date, “ID“) || “ “ || to_char(date, “HH24“) || “ “ || to_char(date, “MI“), …

    Und dann in plotme.txt dieses Format angeben:

    set timefmt „%d %H %M“
    set xdata time

    1. Hallo Jens,

      ja, du hast natürlich Recht. Ich stehe mit Gnuplot etwas auf Kriegsfuß!
      Beim Execute im SQL habe ich nun die Leerzeichen hinzugefügt (ACHTUNG: Zwei Hochkommas auf jeder Seite um die Leerzeichen rum, keine Anführungszeichen!) und im plotme.txt nutze ich nun das Zeitformat für x.
      Die Grafik habe ich ausgetauscht – leider ist es unmöglich bei den Punktebeschriftungen den Tag der Woche loszuwerden, ein substr() geht nicht weil er behauptet $1 ist eine numerische Variable – was aber ja nicht sein kann denn diese beinhaltet ja drei Zahlen. Mit sprintf() bekommt man aber immer nur die erste. Sehr komisch…
      Den Beitrag habe ich aktualisiert.

    1. Hallo Daniel,

      ja, das denke ich auch. Leider hab ich damit noch nicht gearbeitet. Wenn du ne Kurzanleitung schreiben willst: Nur zu! Würde mich freuen!

      1. Was hälst du hiervon? Natürlich ist das ein gutes Stück bequemer 🙂

        import time
        import pytz
        import numpy
        import postgresql
        import matplotlib
        # Make sure matplotlib does not try to use our (not running!) x-Server
        matplotlib.use(‚Agg‘)
        import matplotlib.pyplot as plt

        # Fetch the data from Postgresql
        db = postgresql.open(„pq://benzin:superbenzin@localhost/benzinpreis“)
        ps = db.prepare(„select date, e5 from gas_station_information_history where stid = ‚7b029caf-172e-4e50-6300-f2991c068432‘ and extract(week from date) = (extract(week from now()) – 1) and extract(year from date) = extract(year from now()) order by date asc;“)
        mydata1 = ps()
        ps = db.prepare(„select date, e5 from gas_station_information_history where stid = ‚7b029caf-172e-4e50-6300-f2991c068432‘ and extract(week from date) = (extract(week from now()) – 2) and extract(year from date) = extract(year from now()) order by date asc;“)
        mydata2 = ps()
        db.close()

        # All times are from mydata[0][0] – mydata[len(mydata)-1][0]
        # All prices are from mydata[0][1] – mydata[len(mydata)-1][1]

        # Convert the data to a numpy array
        plotdata1 = numpy.array(mydata1)
        plotdata2 = numpy.array(mydata2)

        # All times are: plotdata1[:,0]
        # All prices are: plotdata1[:,1]

        # Generate the plot figure with size
        fig = plt.figure(figsize = (50, 12))

        # Plot first line
        plt.plot(plotdata1[:,0], plotdata1[:,1], figure = fig)

        # Plot second line
        plt.plot(plotdata2[:,0], plotdata2[:,1], figure = fig)

        # Get the main axis
        ax = plt.gca()

        # Create a Date Format – make sure we’re using our system timezone
        dateformat = matplotlib.dates.DateFormatter(‚%H:%M‘, pytz.timezone(time.tzname[0]))

        # Set the format for the xaxis
        ax.xaxis.set_major_formatter(dateformat)

        # Print every Date and Time
        plt.xticks(numpy.append(plotdata2[:,0], plotdata1[:,0]), rotation = 70, size = 8)

        # Create a grid
        ax.grid(True)

        # Label the plot
        plt.title(‚Letzte zwei Wochen Tankstelle ELO‘)
        plt.ylabel(‚Preis E5‘)
        plt.xlabel(‚Zeit‘)

        # Save the plot
        plt.savefig(‚plot.jpg‘, dpi = (100))

  2. Vielen Dank für deine beiden Artikel und den Verweis auf Tankerkönig!

    Ich würde Stichpunkte nicht in Codeblöcke packen, sondern sie als HTML-Liste einbinden, dann muss man auch nicht mehr horizontal scrollen…

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.