print( max(2.4,2.0) )
print( min(2.4, 2.0) )
print( abs(-2.4) )
print( len("abcde") )
2.4
2.0
2.4
5
Funksjoner er helt sentralt i all slags programmering. Et program uten egendefinerte funksjoner er bare en liste med kommandoer, og det er svært begrenset hva du kan få til med det. Heldigvis er det ekstremt enkelt å lage egne funksjoner i Python.
En vanlig nybegynnerfeil i programmering er å ikke utnytte muligheten til å lage egne funksjoner tilstrekkelig. Hovedregelen er at om det er noe av koden din som repeteres, så skal du lage en ny funksjon som håndterer dette. Målet er at antall linjer i hver funksjon skal være så lite som mulig. Men før vi lærer å lage egne funksjoner, skal vi se litt på hvordan funksjoner brukes.
Python kommer med en rekke innebygde funksjoner. I tillegg kan du importere pakker som har en lang rekke funksjoner. Her er noen innebygde funksjoner i Python:
print( max(2.4,2.0) )
print( min(2.4, 2.0) )
print( abs(-2.4) )
print( len("abcde") )
2.4
2.0
2.4
5
Legg merke til at funksjonene max
, min
, abs
og len
alle er omsluttet av funksjonen print
som sørger for at resultatet skrives ut nedenfor. En funksjon kan altså returnere noe, slik som max
, min
, abs
og len
, eller gjøre noe slik som print
, eller begge deler.
Funksjonen len
angir antall bokstaver, men kan også angi lengde på en liste. En liste i python skriver du som [1,'hei',a]
. En liste kan altså bestå av flere ulike typer variabler og konstanter. De to første elementene her er konstanter, og den tredje er en variabel som er satt lik et flyttal i eksemplet under.
=50.8
aprint([1,'hei',a])
len([1,'hei',a])
[1, 'hei', 50.8]
3
I Python er det svært enkelt å importere ferdig pakker for som kan kjøre ferdige funksjoner. Vi skal her se på et eksempel med noe som heter eksponentialfunksjonen. Denne finnes blant annet i pakken numpy
. Dette er en svært nyttig pakke for matematiske beregninger. Se seksjonen om “Installere Python-pakker” i “0 - installasjon og tips.ipynb” for å installere Numpy.
Som vi skal se lenger ned, så er eksponentialfunksjonen viktig innen økonomifaget fordi den brukes i vekstberegninger, for eksempel for befolkning eller produksjon.
Som nevnt tidligere så er potensen \(y^x\) et grunntall \(y\) opphøyet i en eksponent \(x\). Når vi snakker om eksponenten i bestemt form, så betyr det at grunntallet er et bestemt tall. Dette tallet kalles \(e\), etter matematikeren Euler, og er tilnærmet \(e\approx2.71828\) (det er egentlig en uendelig rekke med siffer etter komma)
Siden Python ikke har noen innebygd funksjon for eksponenten, importerer vi altså først numpy
-pakken, som har en slik funksjon. For å få mindre skriving senere, er det vanlig å importere numpy
som aliaset np
. Dette gjør vi slik:
import numpy as np
Navnet på eksponent-funksjonen i numpy er exp. Skal vi for eksempel finne hva \(e^2=2.71828^2\) er, skriver vi
2) np.exp(
7.38905609893065
Om vi ønsker å vite hva eksponenten er med litt mer enn fem desimaler, kan vi finne eksponenten av 1, siden ethvert tall opphøyd i 1 er seg selv:
1) np.exp(
2.718281828459045
Eksponenten til 5 er for eksempel 2.71828 opphøyet i 5:
1)**5 np.exp(
148.41315910257657
5) np.exp(
148.4131591025766
For å opphøye noe bruker vi altså dobbelt gangetegn **
.
Logartimen er det eksakt motsatte av eksponentialfunksjonen. Tar vi logaritmen av eksponentialfunksjon, får vi argumentet til sistnevnte. Tar vi eksponentialfunksjonen av logartimen får vi også argumentet til logartimen. I en av oppgavene under skal du vise dette.
Vi skal her se hvordan vi kan lage egne funksjoner for renteregning.
La oss starte med å lage en funksjon for et lån uten avdrag. Om banken legger til renten én gang i året og renten er r, så er lånet på x om T år gitt som \(x\cdot (1+r)^{T}\). Dette kan vi programmere som følgende funksjon:
def account_balance(x,r,T):
return x*(1+r)**T
Nøkkelordet return
sørger for at det som står etter return
returneres av funksjonen. Dersom funksjonen ikke har return
i seg, returneres None
, som representerer “ingenting” i Python.
Om renten på lånet er 20%=0.2 og innskuddet er 100, så er beløpet om to år dermed:
100,0.2,2) account_balance(
144.0
La oss nå tenke oss at banken legger til renter n ganger i året. Om for eksempel n=12, så legger banken til renter måndedlig. Vi kan da regne ut lånet ved ulik antall forrentninger per år. Formelen for dette er \(x\cdot (1+\frac{r}{n})^{T\cdot n}\).
Formelen har en enkel og intuitiv forklaring: Vi deler rentesatsen på antall perioder, samtidig som renten legges til i hver periode. Renten i hver periode er dermed \(\frac{r}{n}\), og dette forrentes totalt \(T\cdot n\) ganger. Dette kan programmeres i pyton som
def account_balance_n(x,r,T,n):
return x*(1+r/n)**(T*n)
La oss nå sammenligne å legge til renten hver måned, med å legge den til årlig:
print(account_balance_n(100,0.2,2,12))
print(account_balance_n(100,0.2,2,1))
148.69146179463576
144.0
Vi ser at det lønner seg en god del å legge til renten hver måned. La oss nå anta at rentene legges til hvert sekund i året. Det er 31 536 000 sekunder i et år. I så fall er innskuddet om to år verdt:
100,0.2,2,31536000) account_balance_n(
149.18246858790133
Banken vil altså tjene litt på å gjøre det. I stedet for å regne ut med daglig forrentning kan vi bruke kontinuerlig, som vil si at vi bruker eksponentialfunksjonen
def account_balance_exponential(x,r,T):
return x*np.exp(r*T)
Dette gir oss verdien på lånet når renten legges til “kontinuerlig”, altså oftere enn hvert sekund. La oss nå regne ut verdien av gjelden om to år med kontinuerlig forrentning:
100,0.2,2) account_balance_exponential(
149.18246976412703
Vi ser at dette er omtrent identisk med når vi la til renten hvert sekund. Eksponentialfunksjonen er altså en måte å regne ut vekst når økningen legges til veldig hyppig. Forrentning hvert sekund og kontinuerlig forrentig gir altså omtrent det samme resultatet. “Kontinuerlig forrentning” er et abstrakt begrep, men det betyr i realiteten bare at vi legger til veksten “veldig hyppig”.
Årsaken til at vi ofte bruker logaritmen og eksponentialfunksjonen istedet for å legge til veksten hvert sekund, ar et det er veldig mye enklere å regne med logartimer enn vanlig prosentregning.
La oss definere en enkel funksjon som dobler et tall:
def f(x):
return 2*x
La oss nå sjekke følgende:
print(f(8)) # printer resultatet av funksjonen når argumentet er 8
print(type(f)) # printer hvilken type objekt det er
print(type(f(5))) # printer hvilken type resultate av funksjonen er
16
<class 'function'>
<class 'int'>
Funksjonen dobler argumentet, så med 8 som argument blir resultatet 16. Symbolet f
representerer nå en funksjon (function
), som vi ser er resultatet av print(type(f))
. Men dersom vi kaller funksjonen med for eksemple argumentet 5, får vi resultatet av funksjonen, som er av type int
. Funksjoner trenger imidlertid ikke retunere noe. Dette er også en funksjon:
def hello():
print('Hello world!')
hello()
Hello world!
Vi ser at denne funksjonen ikke har return
-nøkkelordet. Dermed returneres ingen verdi. I stedet for å returnere noe, så gjør denne funksjonen noe, nemlig å skrive ut ‘Hello world”’ til skjermen.
Du kaller funksjoner ved å skrive funksjonen med argumentet i parentes etter navnet. Som vi ser her, når det ikke er noen argumenter, skriver du tomme parenteser.
En funksjon er også en variabel og et objekt. Du kan for eksempel definere en ny variabel som en tidligere definert funksjon:
=hello
hi hi()
Hello world!
Av og til ønsker du at argumentene til en funksjon skal være valgfrie. Da kan du definere en standard (“default”) verdi for argumentet:
def f(x,a=1.02e+2):
return a*x
print(f"Innehaveren krevde i utgangspunkltet {f(5)} kroner for fem epler.\n"
f"Det var alt for mye, så jeg tilbydde {f(5,1.02e-2)} kroner.\n"
f"Vi ble til slutt enige om {f(5,1.02e+1)} kroner.\n")
Innehaveren krevde i utgangspunkltet 510.0 kroner for fem epler.
Det var alt for mye, så jeg tilbydde 0.051000000000000004 kroner.
Vi ble til slutt enige om 51.0 kroner.
Legg merke til at argumentene til en funksjon settes inn i en parentes. For f(x,y)
for eksempel, så er x
og y
argumenter til funksjonen f
. Disse står inne i en parentes (x,y)
. Denne parentesen er et objekt i seg selv og kalles en tuple
. Det går derfor an å definere alle argumentene først, og så la funksjonen evaluere dem. La oss for eksempel si at x=20
og y=50
, slik at (x,y)=(20,50)
. Vi kan da definere variablene først og så evaluere funksjonen på følgende måte
=(20,50)
args*args) f(
1000
Dette er en mulighet som kan være veldig nyttig i mange sammenhenger. En tuple
skiller seg fra en liste (list
) ved at sistnevnte defineres med hakeparenteser []
i stedet for runde parenteser ()
. Vi skal senere komme tilbake til flere forskjeller på en liste og en tuple.
La oss definere en tilbud- og etterspørselsfunksjon. Disse funksjonene skal vise hvilken pris konsumentene maksimalt er villige til å betale for en gitt mengde, og hvor mye produsentene minimum må ha for å selge varen.
Den høyeste prisen er det bare noen få konsumenter som er villige til å betale, så desto større kvantum x
som skal selges, desto lavere må prisen være. Etterspørselsfunksjonen må derfor være fallende (lavere pris ved høyere kvantum).
Her er et forslag til etterspørselsfunksjon:
#Creating a function that returns total demand given a price p
def demand(x):
return 125/(5+x)
Den laveste prisen er det bare den mest effektive bedriften som klarer å produsere lønnsomt for. Ettersom kvantum x
øker vil flere, men mindre effektive bedrifter, tilby varen. Desto større kvantum, desto høyere pris kreves per enhet. Tilbudsfunksjonen bør derfor være stigende (høyere pris ved høyere kvantum).
Her er forslag til tilbudsfunksjoner:
#Creating a function that returns total supply given a price p
def supply(x):
return x**2
Sjekk med nummeriske eksempler at tilbudskurven faktisk er økende og etterspørselskurven faktisk er fallende.
print(supply(5))
print(supply(10))
25
100
Tilbudet er økende
print(demand(5))
print(demand(10))
12.5
8.333333333333334
Etterspørselen er synkende
Finn med prøving og feiling eller på annen måte hvilket kvantum som gir likhet mellom tilbud og etterspørsel. Hvilken pris gir slik kvantum?
#printing out demand and supply for different quantities to see for which the mnarket clears, approximately
print([supply(i) for i in np.linspace(0.,7,10)])
print([demand(i) for i in np.linspace(0,7,10)])
[0.0, 0.6049382716049383, 2.419753086419753, 5.4444444444444455, 9.679012345679013, 15.123456790123456, 21.777777777777782, 29.641975308641978, 38.71604938271605, 49.0]
[25.0, 21.634615384615383, 19.06779661016949, 17.045454545454543, 15.41095890410959, 14.0625, 12.93103448275862, 11.96808510638298, 11.138613861386139, 10.416666666666666]
Markedet klarer rundt en etterspørsel på 14-15 enheter.
def rest(b, x):
return b-x
=1000
b = 700
x f"Av et budjsett på {b} brukte jeg {x} og har igjen {rest(b,x)}"
'Av et budjsett på 1000 brukte jeg 700 og har igjen 300'
time_period
, og en pris per time angitt av argumentet p
. Funksjonen skal forvente at time_period er på formatet “hh:mm:ss” (en streng). Du kan trekke ut timer, minutter og sekunder fra en tekstreng på det foreslåtte formatet, ved å bruke koden hh,mm,ss=int(time_period[0:2]),int(time_period[3:5]),int(time_period[6:])
. Du må konvertere tiden til timer på desimalform for å regne ut kostnad. Det kan du gjøre med denne formelen: tt_dec=hh+mm/60+ss/(60*60)
.def parking_cost(time_period, p):
=int(time_period[0:2]),int(time_period[3:5]),int(time_period[6:])
hh,mm,ss= hh + (mm/60) + (ss/(60*60))
time_decimal return time_decimal*p
= "02:22:31"
time_period = 120
p
print(f"Parkeringstid: {time_period}, pris per time: {p} kroner, parkeringskostnad: {parking_cost(time_period, p)} kroner")
Parkeringstid: 02:22:31, pris per time: 120 kroner, parkeringskostnad: 285.03333333333336 kroner
adj_list
. Funksjonen skal returnere et tilfeldig element fra listen. Du kan for eksempel teste funksjonen med denne listen ['dyr', 'billig', 'pareto optimal', 'høy', 'lav', 'ineffektiv']
. For å plukke et tilfeldig element fra en liste adj_list
med 4
elementer, skriver du adj_list[np.random.randint(4)]
.def get_adjective(adj_list):
= np.random.randint(len(adj_list))
i return adj_list[i]
'dyr', 'billig', 'pareto optimal', 'høy', 'lav', 'ineffektiv']) get_adjective([
'høy'
= 3.234
number = np.exp(number)
e print(np.log(e))
3.234
Vi ser at vi får samme tall
for i in [205,210,220,230, 240]:
print(f"i %: {100*(i/200-1)}, i log: {100*np.log(i/200)}")
i %: 2.499999999999991, i log: 2.4692612590371414
i %: 5.000000000000004, i log: 4.879016416943204
i %: 10.000000000000009, i log: 9.531017980432493
i %: 14.999999999999991, i log: 13.976194237515863
i %: 19.999999999999996, i log: 18.232155679395458
Prosentene ligner, logarigmen er % ved kontinuerlig forrentning.
Lag en funksjon som returnerer det høyeste av funksjonene account_balance_exponential
og account_balance
. Er dette en nyttig funksjon?
def higest_account_balance(x,r,T,n):
return max((
account_balance_n(x,r,T,n),
account_balance_exponential(x,r,T)
))
print(higest_account_balance(100, 0.2, 4, 1))
100, 0.2, 4) account_balance_exponential(
222.55409284924679
222.55409284924679
Funksjonen vil alltid være lik account_balance_exponential
, siden kontinuerlig forrentning alltid er høyest, så ikke så nyttig
eval
til å konvertere verdiene til en tallrekke= eval("1, -5, 10")
values print(max(values))
print(max(values))
print(np.abs(values))
print(len(values))
print(len("1, -5, 10"))
10
10
[ 1 5 10]
3
9
Lengden kan enten være lengden av strengen eller lengden av listen
X/((1+r)**T)
. Lag en funksjon som retuner nåverdien av et beløp. Lag også en funksjon som returnerer nåverdien med kontinuerlig forrentning.def present_value(x,r,T,n):
return x*(1+r/n)**(-T*n)
def present_value_exponential(x,r,T):
return x*np.exp(-r*T)
Du kan bruke en if
- betingelse til å velge hva en funksjon skal gjøre slik:
def pv_or_balance(x,r,T, return_pv ,n=None):
if n is None:
if return_pv:
return present_value_exponential(x,r,T)
else:
return account_balance_exponential(x,r,T)
else:
if return_pv:
return present_value(x,r,T, n)
else:
return account_balance_n(x,r,T,n)
print( pv_or_balance(100, 0.2, 4, True) )
print( pv_or_balance(100, 0.2, 4, False) )
print( pv_or_balance(100, 0.2, 4, True, 1) )
print( pv_or_balance(100, 0.2, 4, False, 1) )
44.932896411722155
222.55409284924679
48.22530864197532
207.35999999999999
Men vi kan også bruke at med negaativ tid T får vi nåverdi, slik at vi slipper et eget argument for det
def pv_or_balance(x,r,T ,n=None):
if n is None:
return account_balance_exponential(x,r,T)
else:
return account_balance_n(x,r,T, n)
print( pv_or_balance(100, 0.2, -4) )
print( pv_or_balance(100, 0.2, 4) )
print( pv_or_balance(100, 0.2, -4, 1) )
print( pv_or_balance(100, 0.2, 4, 1) )
44.932896411722155
222.55409284924679
48.22530864197532
207.35999999999999