'''
Skrevet av: Vegard G. Jervell
Hensikt: Hjelpe folk med å lære alt de ikke lærte i ITGK
Spesifikt: Filbehandling ved bruk av Pandas og NumPy, samt enkel plotting med Matplotlib
'''
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import scipy.optimize as opt

#for å lese inn med numpy
data_enkel = np.genfromtxt('data_files/enkel_fil.txt', skip_header=1) #Hoppe over det første linjen
data_kjipere = np.genfromtxt('data_files/kjip_fil.csv', skip_header=14, delimiter=';') #Spesifisere hva som skiller kolonnene

data_dritt = np.genfromtxt('data_files/drittfil.txt', skip_header=1, invalid_raise=False) #Hvis en linje har feil antall kolonner, bare hopp over linjen.
# legg merke til at du får varsler når den hopper over linjer selv om du har 'invalid_raise=False'
# Det er bare python som gjør deg oppmerksom på at du faktisk har noen linjer den måtte hoppe over :)

t_data, x_data, y_data = data_enkel.transpose() #putter listene med t, x_data, y_data i egne variabler for å spare skriving senere

#For å lese inn med pandas
#Pandas lager 'DataFrame' objekter, disse er forskjellige fra numpy arrays på mange måter!
#Fordelen er at de har mye innebygd funksjonalitet som gjør dem fine når du har store dataset
#Spesielt hvis du har kombinasjoner av tall og bokstaver.
df = pd.read_excel('data_files/excel_fil.xlsx') #leser inn med pandas
t_data, x_data, y_data = df['t'], df['x'], df['y'] #sender hvert sett til en variabel
t_data = t_data.to_list() #konverterer t_list fra en pandas DataFrame til en python liste

#lager et enkelt plot
plt.plot(t_data, x_data, label='x_data')
plt.plot(t_data, y_data, label ='y_data')
plt.legend()
plt.title('enkelt plott')
plt.show()

#nytt plott, denne gangen med aksetitler
plt.plot(x_data, y_data)
plt.xlabel('x_data')
plt.ylabel('y_data')
plt.title('Med aksetitler')
plt.show()

###############################################################
###############################################################
#Gjør litt regresjon på dataen

#Det siste argumentet sier deg graden på regresjonen (førstegrads = lineær, andregrads = kvadratisk osv.)
coeff = np.polyfit(t_data, x_data, 2) #andregradsregresjon y_data = ax^2 + bx + c, coeff[0] = a, coeff[1] = b, coeff[2] = c

t_akse = np.linspace(min(t_data), max(t_data), 1000) #lager en liste med 1000 verdier fra min(t_data) til max(t_data)
y_reg = coeff[0] * t_akse ** 2 + coeff[1] * t_akse + coeff[2] #regner ut punkter fra regresjonen
y_regresjon = np.polyval(coeff, t_akse) #denne gjør akkurat det samme som linjen over, men med mindre skriving :)

#Plotter data og regresjon, gir navn til linjene
plt.plot(t_data, x_data, label ='data')
plt.plot(t_akse, y_regresjon, label ='regresjon')
plt.legend() #Denne gjør at navnene vises
plt.xlabel('t')
plt.ylabel('x_data')
plt.title('Dette plottet blir lagret')
plt.savefig('plots/forsok1') #lagrer plottet i mappen 'plots', NB: gjør dette før plt.show()
plt.show()

# Nå skal vi gjøre regresjon med noe som ikke er et polynom :)

def fit_func(x, a, b, c):
    return a * np.sin(b * x + c) #En generalisert sinusfunksjon

#fit_coeff, cov = opt.curve_fit(fit_func, x_data, y_data, p0=[1,1,1]) #funker ikke fordi gjetningene er dårlig

# NB: Legg merke til at det står 'fit_func' og IKKE 'fit_func()'
# Forskjellen er at det første peker til funksjonen 'fit_func' mens det andre evaluerer funksjonen 'fit_func'
fit_coeff, cov = opt.curve_fit(fit_func, x_data, y_data, p0=[1, 0.1, 0])
#fit_coeff er en liste med de tilpassede koeffisientene [a, b, c]
#cov er kovariansmatrisen til koeffisientene (se:statistikk) hint: diagonalen er variansen til hver koefisient

x_akse = np.linspace(min(x_data), max(x_data), 100)
regresjon = fit_func(x_akse, fit_coeff[0], fit_coeff[1], fit_coeff[2])

vx = np.diff(x_data)/np.diff(t_data) #regner ut hastigheten i x-retning NB: denne blir ett element kortere enn x_data
vy = np.diff(y_data)/np.diff(t_data) #regner ut hastigheten i y-retning

sigma_x = 1 + 0.1 * vx #usikkerhet i x (bare en funksjon jeg fant på)
sigma_y = 0.1 + 0.05 * vy #usikkerhet i y NB: fordi vy er ett element kortere enn y_data, blir også sigma_y det

sigma_regresjon = 0.3 # finner på en usikkerhet i regresjonen, vanligvis regner du ut denne som et konfidensintervall
                      # ved å bruke standardavvikene fra kovariansmatrisen, men det gidder jeg ikke nå

#Lager plot med usikkerhet
#capsize = 3 gjør at kryssene blir pene, linstyle='' gjør at det ikke kommer en linje mellom errorbarsene
#Legg merke til x_data[:-1]. Fordi sigma_x er ett element kortere enn x_data må vi klippe bort det bakerste eller første punktet.
plt.errorbar(x_data[:-1], y_data[:-1], yerr=sigma_y, xerr=sigma_x, color='black', label='Data', capsize=3, linestyle='')
plt.plot(x_akse, regresjon, label='Regresjon', color='b')

#Fill between fyller ut området mellom andre og tredje argument
# alpha=0.3 gjør området ganske gjennomsiktig (0 = helt gjennomsiktig, 1 = ikke gjennomsiktig i det hele tatt)
# linewidth = 0 gjør at det ikke kommer linjer på toppen og bunnen.
plt.fill_between(x_akse, regresjon - sigma_regresjon, regresjon + sigma_regresjon, alpha=0.3, color='b', linewidth=0)
plt.legend() #ikke glem denne
plt.xlabel('x_data')
plt.ylabel('y_data')

plt.title(r'Dette er så fænsi at jeg skriver $L_aT^ex$ i aksetittelen')
plt.show()

#litt mer regresjon og errorbars
data_usikker = np.genfromtxt('data_files/usikker_data.txt', skip_header=3)

x_data, y_data = data_usikker.transpose()
sigma_x = 0.8
sigma_y = 1.2

coeff= np.polyfit(x_data, y_data, 1)
x_axis = np.linspace(min(x_data), max(x_data), 50)
y_reg = np.polyval(coeff, x_axis)

plt.errorbar(x_data, y_data, xerr=sigma_x, yerr=sigma_y, capsize=2, color='black', linestyle='')
plt.plot(x_axis, y_reg, label='reg')

plt.legend()
plt.show()

