Histogram o zmiennej szerokości

Użytkowanie arkusza kalkulacyjnego
pachnis
Posty: 2
Rejestracja: czw maja 16, 2019 10:07 am

Histogram o zmiennej szerokości

Post autor: pachnis »

Witam, potrzebuję stworzyć histogram w którym szerokość kolumn jest zmienna. Ma to wyglądać mniej więcej tak jak na załączonym poniżej obrazku. Czy ktoś wie może czy i jak można to zrobić?
Załączniki
histogram zmienny.PNG
LibreOffice Wersja: 6.1.4.2 na Windows 10
Jan_J
Posty: 4560
Rejestracja: pt maja 22, 2009 1:20 pm
Lokalizacja: Wrocław

Re: Histogram o zmiennej szerokości

Post autor: Jan_J »

Ha! to jest jeden z moich ulubionych przykładów. Na taki wykres nie ma gotowca w Excelu ani w Calcu. Pokazuję czasem takie problemy studentom dla zachęty do uczenia się Matlaba, Scilaba, R-a, Pythona, albo choćby Gnuplota.

Skrypt w Python/Matplotlib mogę odgrzebać dość łatwo. Integracja z Calcem nie będzie prosta, ale przygotowanie grafiki na podstawie danych z pliku ods już tak. Z tym, że nie z poziomu Calc, tylko przez odczyt pliku innym narzędziem.

Zgodnie z pow. obietnicą, podsyłam mój `firmowy` przykład generowania podobnych wykresów funkcją pyplot.bar w Python + Matplotlib.
Źródła danych (do wyboru):
woj.zip
baza SQLite (do rozpakowania)
(40.96 KiB) Pobrany 84 razy
albo
woj.txt
plik TSV
(531 Bajtów) Pobrany 91 razy
Dane pochodzą -- już nie pamiętam -- albo z GUS-u, albo z statoids.com.

Kod skryptu

Kod: Zaznacz cały

#!/usr/bin/env python3

""" 
    tworzenie wykresu na podstawie danych z bazy SQLite
    przy użyciu pakietu Matplotlib/PyPlot 
"""

import sqlite3
from matplotlib import pyplot
from matplotlib.font_manager import FontProperties


def z_bazy(rok):

    baza = './woj.db'

    # parametry zapytania
    q = ''' 
    select wojewodztwo as "Województwo", 
        powierzchnia as "Powierzchnia", 
        ludnosc as "Ludność" 
    from wojewodztwa
        join wojpow using(klwoj)
        join wojlud using(klwoj)
    where rok = ?
    order by ludnosc / powierzchnia desc; 
    '''

    # nawiązanie połączenia
    conn = sqlite3.connect(baza)
    cursor = conn.cursor()
    cursor.execute(q, (rok,))
    data = cursor.fetchall()
    conn.close()
    return data

def z_pliku(rok, sep = '\t'):
    nazwa = './woj.txt'
    with open(nazwa) as f:
      data = [ ]
      f.readline()
      for woj in f:
        woj = woj.split(sep)
        woj[1] = float(woj[1])
        woj[2] = float(woj[2])
        data.append(woj)
    return data

def przygotuj(dane):
    ludPL = 0
    powPL = 0.0
    powWoj = []
    ludWoj = []
    nazWoj = []
    midWoj = []
    poczWoj = [0.0]
    for woj in dane:
        powWoj.append(woj[1])
        poczWoj.append(powPL + woj[1])
        midWoj.append(powPL + 0.5*woj[1])
        ludWoj.append(woj[2]/woj[1])
        nazWoj.append(woj[0])
        powPL += woj[1]
        ludPL += woj[2]
    ludPL = ludPL/powPL
    poczWoj.pop(-1)
    return poczWoj, midWoj, powWoj, ludWoj, nazWoj, powPL, ludPL

def rysuj(dane, rok):

    # obróbka odpowiedzi
    poczWoj, midWoj, powWoj, ludWoj, nazWoj, powPL, ludPL = przygotuj(dane)

    # przygotowanie wykresu
    fontFace = 'DejaVu Sans'

    fopis = FontProperties()
    fopis.set_family(fontFace)
    fopis.set_size(8)

    fnaglowek = FontProperties()
    fnaglowek.set_family(fontFace)
    fnaglowek.set_size(12)

    kolory = ('#a0a0ff', '#ffa0a0')
    opisy = ('województwa %s' % rok, 'Polska %s' % rok )

    fig = pyplot.subplot(111)
    fig.set_xlim(0.0, powPL)
    fig.set_ylim(0.0, 500.0)
    fig.set_title('Zaludnienie województw Polski', fontproperties = fnaglowek)
    fig.set_xlabel('Powierzchnia', fontproperties = fnaglowek)
    fig.set_ylabel('Gęstość zaludnienia [os./km^2]', fontproperties = fnaglowek)
    pyplot.xticks(midWoj, nazWoj, fontproperties = fopis, rotation = 90)
    fig.grid(axis = 'y')

    # kreślenie serii danych
    fig.bar(poczWoj, ludWoj, width = powWoj, align = "edge", color = kolory[0], edgecolor = 'black', label = opisy[0])
    fig.bar((0.0), (ludPL), width=powPL, align = "edge", color = kolory[1], edgecolor = 'black', alpha = 0.2, label = opisy[1])

    # legenda
    fig.legend()
    ltext = pyplot.gca().get_legend().get_texts()
    pyplot.setp(ltext[0], fontproperties = fopis, color = kolory[0])
    pyplot.setp(ltext[1], fontproperties = fopis, color = kolory[1])

rok = 2007
dane = z_bazy(rok)
rysuj(dane, rok)
pyplot.tight_layout()
pyplot.show()
oraz otrzymany z niego obrazek PNG
plwoj.png
W razie potrzeby służę pomocą przy sczytaniu danych bezpośrednio z pliku Calca. Obrazki mogą powędrować do odrębnych plików graficznych; teoretycznie dałoby się je automatycznie osadzić w dokumencie, ale wymaga to grzebaniny. Gdyby obrazków było 200, zalecałbym podjęcie wysiłku; przy 20 raczej nie.
Możliwe jest też użycie podobnego skryptu jako makra w Calcu, choć wymaga to przekonfigurowania Pythona załączonego do LibreOffice (żeby doinstalować Matplotlib + zależności).

Ale zdaje mi się, że Twój wykres ma wadę konstrukcyjną. Histogram ma być graficznym przybliżeniem gęstości rozkładu, więc na osi liczbowej winna być zaznaczona intensywność. Jeśli jest to częstość bezwzględna podawana w dniach, to łączenie klas w szersze spowoduje wydłużanie słupków. A pożądane byłoby raczej ich uśrednianie.
JJ
LO (7.6|24.2) ∙ Python (3.12|3.10) ∙ Unicode 15 ∙ LᴬTEX 2ε ∙ XML ∙ Unix tools ∙ Linux (Rocky|CentOS)
pachnis
Posty: 2
Rejestracja: czw maja 16, 2019 10:07 am

Re: Histogram o zmiennej szerokości

Post autor: pachnis »

Dzięki wielkie, będę kombinował :D
LibreOffice Wersja: 6.1.4.2 na Windows 10
ODPOWIEDZ