Tämä ohjelma laskee Gini-kertoimen käyttäjän antamista tuloista. Gini-kerroin kuvaa tuloeroja ja se on 0-1 välillä. Gini-kerroin lasketaan kaavasta A/(A+B) tai A/0,5. Tässä lasketaan A:n pinta-ala ja lasketaan kaavasta A/0,5 Gini-kerroin. Gini-kerroin voidaan esittää graafisesti, jossa on kaksi käyrää toinen käyrä kuvaa, kun tulot menevät tasan ja toinen käyrä kuvaa todellista tilannetta A on näiden välissä. B on A:n alapuolella. Näin ollen A+B on 0,5.
from tkinter import * from tkinter.ttk import * import tkinter as tk import tk_tools from matplotlib.figure import Figure from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk) import matplotlib.pyplot as plt import numpy as np from itertools import accumulate from operator import add root = Tk() def laskenta(tulot): tulot.insert(0,0) i = len(tulot)-1 #Lasketaan gini-kerroin if (len(tulot) > 0): tulot.sort() summa = 0.0 summa = sum(tulot) #Lasketaan osuudet ja kumulatiiviset osuudet lambdalla ja accumulatella osuus = list(map(lambda x: x/summa, tulot)) kumulatiivinen = list(accumulate(osuus,add)) #Lasketaan pinta_ala pinta_ala = [] pinta_ala = list(map(lambda x: (1.0/i)*((kumulatiivinen[x-1]+kumulatiivinen[x])/2), range(1,len(kumulatiivinen)))) pa_summa = sum(pinta_ala) gini = (0.5-pa_summa)/0.5 return round(gini*10000.0)/10000,kumulatiivinen def piirraKayrat(tulot,kumulatiivinen): uusi = Toplevel(root) uusi.title("New Window") uusi.geometry("500x500") fig = Figure(figsize=(5,5),dpi=100) #Lasketaan luvut käyrää varten. i = len(tulot)-1 y = [a/i for a in range(0,i+1)] y2 = kumulatiivinen #Piirretään käyrät. plot1 = fig.add_subplot(111) plot1.plot(y) plot1.plot(y2) canvas = FigureCanvasTkAgg(fig,master=uusi) canvas.get_tk_widget().grid() def poista(event): #Tuplaklikkaaminen poistaa tiedon. valinta = lst.curselection() lst.delete(valinta) lst2.delete(valinta) matala = 0 keskitaso = 0 korkea = 0 def pylvas(tulot,kumulatiivinen): i = len(tulot)-1 N = len(kumulatiivinen) ind = np.arange(N) width = 0.35 fig = plt.figure() ax = fig.add_axes([0,0,1,1]) ax.bar(ind,kumulatiivinen,width,color='b') ax.set_ylabel("Kumulatiivinen") ax.legend("tulot") plt.show() def Kayttoliittyma(): global korkea,keskitaso,matala #Syötetään tulo sekä niiden määrä ja lisätään se summaan. syote = e1.get().split(',') try: tulot = [float(s) for s in syote] valinta = int(CheckVar.get()) gini,kumulatiivinen = laskenta(tulot) if (gini > 0.4): label.config(text=str(gini),bg="red") korkea += 1 elif (gini > 0.3): label.config(text=str(gini),bg="yellow") keskitaso += 1 else: label.config(text=str(gini),bg="green") matala += 1 label2.config(text=str(korkea)) label3.config(text=str(keskitaso)) label4.config(text=str(matala)) if (valinta == 1): #Piirretään piirraKayrat(tulot,kumulatiivinen) pylvas(tulot,kumulatiivinen) lst.insert(END, "Gini-kerroin:" + str(gini)) lst2.insert(END, str(tulot)) except: e1.config(bg="red") frame = Frame(root) frame.pack() frame2 = Frame(root) frame2.pack() frame3 = Frame(root) frame3.pack() frame4 = Frame(root) frame4.pack() var = StringVar() CheckVar = IntVar() #Luodaan kontrollit. l1 = Label(frame,textvariable=var) l1.grid(row=1,column=1) e1 = tk.Entry(frame,bg="white") e1.grid(row=1,column=2) var.set("Tulo:") #Luodaan listaan skrollaava toiminto scroll = Scrollbar(frame3) scroll.pack(side=RIGHT,fill=Y) lst = Listbox(frame3, yscrollcommand=scroll.set) lst.pack(side=LEFT,fill=BOTH) scroll.config(command=lst.yview) lst.bind('<Double-1>',poista) scroll2 = Scrollbar(frame4) scroll2.pack(side=RIGHT,fill=Y) lst2 = Listbox(frame4,yscrollcommand=scroll2.set) lst2.pack(side=LEFT,fill=BOTH) scroll2.config(command=lst2.yview) btn = Button(frame2,text="Laske",command=Kayttoliittyma) btn.grid() check1 = Checkbutton(frame2,text="Piirrä",variable=CheckVar,onvalue=1,offvalue=0) check1.grid() label = tk.Label(frame2,text="teksti",bg="white") label.grid() label2 = tk.Label(frame2,text="",bg="red") label2.grid() label3 = tk.Label(frame2,text="",bg="yellow") label3.grid() label4 = tk.Label(frame2,text="",bg="green") label4.grid() root.mainloop()
koodari kirjoitti:
Gini-kerroin lasketaan kaavasta A/(A+B) tai A/0,5.
Olisi hirmu hienoa, jos kertoisit myös, kenen tuloeroja se Gini-kerroin kuvaa ja millä tavalla sitä tulkitaan ja mitä ovat kaavassa A ja B (eli mistä ne tulevat). Koodissa olisi järkevää erottaa jotenkin tuo laskenta ja käyttöliittymä, ja koko käyttöliittymän voisi tehdä samalla tavalla (eli joko tekstinä tai Tk-käyttöliittymänä, ei sekaisin).
Selvästikin se laskee annetun tulojoukon Gini-kertoimen. Eli jos ohjelmalle syötettäisiin jokaisen suomalaisen tulot, niin tuloksena olisi suomalaisten Gini-kerroin.
Lisää aiheesta
https://fi.wikipedia.org/wiki/Gini-kerroin
Paransi koodia. Nyt se on kokonaan toteutettu Tk-käyttöliittymänä ja lisäksi se piirtää Lorenz-käyrän.
koodaaja kirjoitti:
Paransi koodia. Nyt se on kokonaan toteutettu Tk-käyttöliittymänä ja lisäksi se piirtää Lorenz-käyrän.
Jos tarkoitus on demota käyttöliittymän tekoa TK:lla, niin kannattaa toteuttaa se järjellisesti. Nyt käyttöliittymässä ei ole selitteitä missään. Mitä syötetään ja mitä ovat nuo ruudukkoon tulevat summat? Lisäksi esimerkiksi kuvaajan piirto on nimellä "Uusi ikkuna" ja jos arvoja ei ole syötetty niin tuon toiminnon seurauksena on "ZeroDivisionError".
Ohjelmaa on hieman parannettu. Lisäksi taulukko on korvattu poistettavilla tekstikentillä.
koodaaja kirjoitti:
Ohjelmaa on hieman parannettu. Lisäksi taulukko on korvattu poistettavilla tekstikentillä.
Vieläkin voisit tehdä skaalautuvan käyttöliittymän ja järkeistää sijoittelun. Eikö noita poistettavia tekstikenttiä saa jonkun freimin sisälle mikä olisi varustettu vierityspalkilla. Sillä tavalla ohjelmaikkunan koko ei kasvaisi automaattisesti kun arvoja syötetään. Lisäksi itse ohjelmaan pitäisi lisätä syötteen tarkistus siten ettei ohjelmaa voisi saada virheelliseen tilaan.
Oma keskeneräinen tuotokseni, johon lisäsin syötekentän validoinnin ja rajojen korostamisen punaisella kun syöte on virheellinen.
Korvasin poistettavat tekstikentät listalla, josta voi tuplaklikkaamalla ottaa tiedon pois. Lisäksi listassa on skrollaustoiminto.
koodaaja kirjoitti:
Korvasin poistettavat tekstikentät listalla, josta voi tuplaklikkaamalla ottaa tiedon pois. Lisäksi listassa on skrollaustoiminto.
Täällä on minun versioni 8th:lla. Kuvaajanpiirtoa en toteuttanut ja enterin painallus olisi varmaan hyvä linkittää lisäys nappiin. Käyttöliittymä on Nuklear pohjainen, eli käyttöliittymän tila pitää tallentaa itse ja esimerkiksi listan toiminnalisuus pitää toteuttaa itse. Omaan listaani toteutin lisäyksen, valinnan ja poiston.
Syöttöä on muokattu siten, että kaikki tulot kerrotaan kerralla ja tulot on erotettu pilkuilla.
koodaaja kirjoitti:
Syöttöä on muokattu siten, että kaikki tulot kerrotaan kerralla ja tulot on erotettu pilkuilla.
Edelleenkään et tarkista syötettä ja lopputulos on huono, jos käyttäjä syöttää vahingossa vääränlaisen syötteen. Lisäksi käyttöliittymä vaatisi hienosäätöä. Esimerkiksi oletuksena se ei mahdu kokonaan ikkunaan.
Toteutin omaan versiooni syötekentän validoinnin tilakoneen avulla. Paransin myös käyttöliittymän toimivuutta. Video toiminnasta nähtävillä täällä.
Ehdotin aiemmin, että erottaisit laskennan ja käyttöliittymän. Nyt edelleen jokaisessa laskentaan liittyvässä funktiossa on myös käyttöliittymän asioita. Lasket myös samoja asioita kahteen kertaan (mm. osuus ja kumulatiivinen) ja käytät laskuissa globaaleja muuttujia aiheettomasti (erityisesti i, joka on vain taulukon koko!), ja ainakin tulo ja vaesto ovat ilmeisesti käyttämättömiä. Pythonissa voi muuten käyttää myös ä-kirjainta nimissä.
Lisäksi tuollainen for-silmukoiden käyttö summaan ja listan muuttamiseen on kömpelöä, jos voisi käyttää suoraan yhden rivin koodeja:
summa = sum(tulot) osuus = [x / summa for x in tulot]
Seuraavat parannukset tehty: for-silmukat muutettu lambdaksi ja käyttöliittymä ja laskenta on erotettu.
Hienosti löysit myös accumulate-funktion, jota en itse muistanut suoraan. Eli ilmeisesti taitoa löytyisi, kun viitsisit keskittyä ja miettiä asiat alusta loppuun. Voisit tehdä paljon hyödyllisempiä vinkkejä ja kehittyä samalla itse enemmän, kun yrittäisit tehdä oikein selvää ja huolellista koodia.
Kommentoin jo turhista muuttujista, vieläkin näitä turhia rivejä vilisee:
# ei käytetä: tulo = 1 i = 0 # aina tosi, ja vaikka ei olisi, ihan sama: if (len(tulot) > 0): # turhia sijoituksia, kun arvot korvataan heti: summa = 0.0 # summa = sum(tulot) pinta_ala = [] # pinta_ala = list(map(...))
Voisit supistaa pinta-alan kaavaa, kun et tarvitse alaa taulukkona. Tuostahan tulee vain:
pa = (sum(kumulatiivinen) - kumulatiivinen[-1]/2) / i
Voisit vielä poistaa tulot-taulukosta ylimääräisen nollan alusta. Sehän on virhe, ylimääräinen datapiste. Missään nimessä laskenta-funktion ei pitäisi muuttaa annettua dataa ulos päin, se on yllättävä vaikutus ja huono toimintatapa. Ilmeisesti olet laittanut nollan, jotta pinta-alan lasku toimisi oikein, mutta yllä olevalla kaavan päivityksellä nollaa ei enää tarvita, tai jos haluat sen, voit lisätä sen kumulatiivisen listan alkuun.
kumulatiivinen = [0] + list(...)
Tuo i-nimi on hieman häiritsevä, ei kovin kuvaava tuossa käytössä, yleisesti käytössä indeksin merkkinä. Jos poistat ylimääräisen nollan tuloista, i on len(tulot) ja voit luopua tästäkin ylimääräisestä muuttujasta. Tai kuvaava nimi olisi vaikka (populaation) koko.
Aihe on jo aika vanha, joten et voi enää vastata siihen.