Skip to content

Commit 0711f8e

Browse files
authored
Merge pull request #122 from statisticsnorway/dash_test
small changes
2 parents ff66cea + 9a95fe4 commit 0711f8e

File tree

17 files changed

+170
-219
lines changed

17 files changed

+170
-219
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "ssb-konjunk"
33
version = "2.1.0"
44
description = "SSB Konjunk 422"
55
authors = [
6-
{name = "Johanne Saxegaard", email = "jox@ssb.no"},
6+
{name = "Johanne Saxegaard", email = "jox@ssb.no"},
77
{name = "Halvor Steffenssen", email = "hvr@ssb.no"}
88
]
99
license = "MIT"

src/ssb_konjunk/dash/calculations/calc_data.py

Lines changed: 46 additions & 74 deletions
Large diffs are not rendered by default.

src/ssb_konjunk/dash/calculations/helper_functions.py

Lines changed: 41 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import datetime
2-
import locale
2+
from datetime import date
3+
from datetime import datetime
34
from typing import Literal
5+
46
import pendulum
57
import polars as pl
6-
from datetime import datetime, date
8+
79

810
def monthdelta(d1: datetime, d2: datetime):
911
"""Finner differansen mellom to måneder"""
@@ -16,13 +18,13 @@ def parse_period(period: str) -> datetime:
1618
"""Parser en periode til datetime"""
1719
return datetime.strptime(period, "%Y-%m")
1820

21+
1922
def multi_join(
2023
dfs: list[pl.DataFrame],
2124
on: str,
2225
how: Literal["left", "right", "inner", "cross", "semi", "anti"] = "left",
2326
):
24-
"""
25-
Slår sammen flere Polars DataFrames sekvensielt på en spesifisert kolonne.
27+
"""Slår sammen flere Polars DataFrames sekvensielt på en spesifisert kolonne.
2628
2729
Utfører en kjedet sammenslåing (join) av flere DataFrames i en gitt liste basert på én felles kolonne.
2830
Hver DataFrame legges til én etter én, med spesifisert join-type og automatisk suffiks for overlappende kolonnenavn.
@@ -35,7 +37,6 @@ def multi_join(
3537
Returns:
3638
pl.DataFrame: Ett samlet DataFrame etter sekvensiell join av alle inputtabellene.
3739
"""
38-
3940
df = dfs[0]
4041

4142
for idx, i in enumerate(dfs[1:]):
@@ -53,8 +54,7 @@ def __init__(
5354
internal_col: str = "avg",
5455
dt_out_format: str = "%b %Y",
5556
) -> None:
56-
"""
57-
Representerer en datakilde tilrettelagt for tidsbasert analyse og gruppering.
57+
"""Representerer en datakilde tilrettelagt for tidsbasert analyse og gruppering.
5858
5959
Denne klassen organiserer et Polars DataFrame ved å sortere på en datokolonne
6060
og gjør det mulig å gruppere og analysere utvalgte kolonner.
@@ -75,7 +75,6 @@ def __init__(
7575
internal_col (str, valgfritt): Internt kolonnenavn brukt for aggregering. Standard er "avg".
7676
dt_out_format (str, valgfritt): Format for datoer ved visning. Standard er "%b %Y".
7777
"""
78-
7978
self.data = data.sort(date_col)
8079
self._date = date_col
8180
self._col = col_name
@@ -84,25 +83,22 @@ def __init__(
8483
self._dt_out_format = dt_out_format
8584

8685
def latest_date(self) -> None | datetime:
87-
"""
88-
Henter den siste datoen fra datakolonnen.
86+
"""Henter den siste datoen fra datakolonnen.
8987
9088
Returns:
91-
datetime | None: Den siste datoen som finnes i datasettet,
89+
datetime | None: Den siste datoen som finnes i datasettet,
9290
eller None dersom verdien ikke er en gyldig datetime.
9391
"""
94-
9592
latest = self.data.get_column(self._date).last()
9693
if isinstance(latest, datetime):
9794
return latest
98-
elif isinstance(latest, date): # <- use date directly
95+
elif isinstance(latest, date): # <- use date directly
9996
return datetime.combine(latest, datetime.min.time())
10097
else:
10198
return None
10299

103100
def _percent_change(self, series_1: pl.Expr, series_2: pl.Expr):
104-
"""
105-
Beregner prosentvis endring mellom to serier.
101+
"""Beregner prosentvis endring mellom to serier.
106102
107103
Args:
108104
series_1 (pl.Expr): Første uttrykk / serie.
@@ -111,25 +107,21 @@ def _percent_change(self, series_1: pl.Expr, series_2: pl.Expr):
111107
Returns:
112108
pl.Expr: Et uttrykk som representerer prosentvis endring.
113109
"""
114-
115110
return ((series_1 - series_2) / series_2) * 100
116111

117112
def _create_date(self, date: datetime) -> str:
118-
"""
119-
Formaterer en datetime til en str med definert utdataformat.
113+
"""Formaterer en datetime til en str med definert utdataformat.
120114
121115
Args:
122116
date (datetime): Datoen som skal formateres.
123117
124118
Returns:
125119
str: Formatert og kapitalisert dato som tekst.
126120
"""
127-
128121
return date.strftime(self._dt_out_format).capitalize()
129122

130123
def _gen_header(self, n: int, skip: int = 0):
131-
"""
132-
Genererer en overskrift som viser datoperioden basert på antall perioder og hopp.
124+
"""Genererer en overskrift som viser datoperioden basert på antall perioder og hopp.
133125
134126
Args:
135127
n (int): Antall måneder per periode.
@@ -138,15 +130,13 @@ def _gen_header(self, n: int, skip: int = 0):
138130
Returns:
139131
str: En str som representerer datoperioden (eks. "Jan 2023 - Mar 2023").
140132
"""
141-
142133
dates = self.data.get_column(self._date).unique().sort()
143134
latest: datetime = dates[-1 - (n * skip)]
144135
oldest: datetime = dates[-1 - ((n * skip) + n)]
145136
return f"{self._create_date(oldest)} - {self._create_date(latest)}"
146137

147138
def _base(self, n: int, *agg, **named_aggs):
148-
"""
149-
Utfører aggregering over dynamiske tidsvinduer og grupper.
139+
"""Utfører aggregering over dynamiske tidsvinduer og grupper.
150140
151141
Args:
152142
n (int): Størrelsen på tidsvinduet i måneder.
@@ -166,8 +156,7 @@ def _base(self, n: int, *agg, **named_aggs):
166156
)
167157

168158
def _base_w_header(self, n: int, skip: int = 0, *agg, **named_aggs):
169-
"""
170-
Genererer en overskrift for perioden og returnerer resultatet fra baseaggregering.
159+
"""Genererer en overskrift for perioden og returnerer resultatet fra baseaggregering.
171160
172161
Kombinerer datoperiode-headeren med aggregerte resultater fra `_base`.
173162
@@ -185,8 +174,7 @@ def _base_w_header(self, n: int, skip: int = 0, *agg, **named_aggs):
185174
return header, result
186175

187176
def n_month(self, n: int, skip: int = 0) -> tuple[str, pl.DataFrame]:
188-
"""
189-
Henter siste tilgjengelige verdi for gjennomsnittskolonnen for en periode.
177+
"""Henter siste tilgjengelige verdi for gjennomsnittskolonnen for en periode.
190178
191179
Args:
192180
n (int): Antall måneder i perioden.
@@ -195,14 +183,12 @@ def n_month(self, n: int, skip: int = 0) -> tuple[str, pl.DataFrame]:
195183
Returns:
196184
tuple[str, pl.DataFrame]: En tuple med overskrift og filtrert datasett med siste verdi.
197185
"""
198-
199186
return self._base_w_header(
200187
n, skip, **{self._avg: pl.col(self._avg).get(-1 - skip)}
201188
)
202189

203190
def n_month_percent(self, n: int, skip: int = 0) -> tuple[str, pl.DataFrame]:
204-
"""
205-
Beregner prosentvis endring mellom to perioder og returnerer med overskrift.
191+
"""Beregner prosentvis endring mellom to perioder og returnerer med overskrift.
206192
207193
Args:
208194
n (int): Antall måneder i perioden.
@@ -211,7 +197,6 @@ def n_month_percent(self, n: int, skip: int = 0) -> tuple[str, pl.DataFrame]:
211197
Returns:
212198
tuple[str, pl.DataFrame]: En tuple med periodebeskrivelse og datasett med prosentendringer.
213199
"""
214-
215200
return self._base_w_header(
216201
n,
217202
skip,
@@ -223,8 +208,7 @@ def n_month_percent(self, n: int, skip: int = 0) -> tuple[str, pl.DataFrame]:
223208
)
224209

225210
def n_percent_rolling(self, n: int, skip: int = 0):
226-
"""
227-
Beregner rullerende prosentvis endring over en periode og returnerer med datoperiode-header.
211+
"""Beregner rullerende prosentvis endring over en periode og returnerer med datoperiode-header.
228212
229213
Denne metoden bruker en rullerende tidsvinduanalyse for å beregne prosentvis endring
230214
mellom første og siste verdi i hvert vindu, og gir samtidig en datoperiodebeskrivelse.
@@ -238,15 +222,16 @@ def n_percent_rolling(self, n: int, skip: int = 0):
238222
tuple[str, pl.DataFrame]: En tuple med en tekstlig overskrift for datoperioden og et DataFrame
239223
med prosentvis endring for hver gruppe.
240224
"""
225+
241226
def _gen_header(n: int, skip: int = 0):
242-
'''Lager overskrift for hver perioden'''
227+
"""Lager overskrift for hver perioden"""
243228
dates = self.data.get_column(self._date).unique().sort()
244229
latest: datetime = dates[-1 - (skip)]
245230
oldest: datetime = dates[-1 - ((skip) + n - 1)]
246231
return f"{self._create_date(oldest)} - {self._create_date(latest)}"
247232

248233
def map_test(x: pl.DataFrame):
249-
'''Lager det rullende gjennomsnittet for hver periodegruppe'''
234+
"""Lager det rullende gjennomsnittet for hver periodegruppe"""
250235
if x.shape[0] != n:
251236
x = x.with_columns(
252237
**{self._avg: pl.col(self._col).fill_null(strategy="backward")}
@@ -276,8 +261,7 @@ def map_test(x: pl.DataFrame):
276261
)
277262

278263
def n_mean_rolling(self, n: int, skip: int = 0):
279-
"""
280-
Beregner et rullerende gjennomsnitt for hver gruppe i datasettet og returnerer med datoperiode-header.
264+
"""Beregner et rullerende gjennomsnitt for hver gruppe i datasettet og returnerer med datoperiode-header.
281265
282266
Denne metoden beregner gjennomsnittet av verdiene innenfor et rullerende vindu på `n` måneder.
283267
Hvis et vindu inneholder færre enn `n` datapunkter, blir manglende verdier fylt bakover.
@@ -293,25 +277,22 @@ def n_mean_rolling(self, n: int, skip: int = 0):
293277
"""
294278

295279
def _gen_header(n: int, skip: int = 0):
296-
'''Lager overskrift for hver perioden'''
280+
"""Lager overskrift for hver perioden"""
297281
dates = self.data.get_column(self._date).unique().sort()
298282
latest: datetime = dates[-1 - (skip)]
299283
oldest: datetime = dates[-1 - ((skip) + n - 1)]
300284
return f"{self._create_date(oldest)} - {self._create_date(latest)}"
301285

302286
def map_test(x: pl.DataFrame):
303-
'''Lager det rullende gjennomsnittet for hver periodegruppe'''
287+
"""Lager det rullende gjennomsnittet for hver periodegruppe"""
304288
if x.shape[0] != n:
305289
x = x.with_columns(
306290
**{self._avg: pl.col(self._col).fill_null(strategy="backward")}
307291
)
308292
else:
309-
x = x.with_columns(
310-
**{
311-
self._avg: pl.col(self._col).mean()
312-
}
313-
)
293+
x = x.with_columns(**{self._avg: pl.col(self._col).mean()})
314294
return x
295+
315296
return _gen_header(n, skip), (
316297
self.data.rolling(
317298
pl.col(self._date),
@@ -329,11 +310,10 @@ def map_test(x: pl.DataFrame):
329310
def n_month_rolling_percent_compare(
330311
self, n: int, skip: int = 0, skip_1: int = 1
331312
) -> tuple[str, pl.DataFrame]:
332-
"""
333-
Sammenligner rullerende gjennomsnitt mellom to perioder og beregner prosentvis endring.
313+
"""Sammenligner rullerende gjennomsnitt mellom to perioder og beregner prosentvis endring.
334314
335-
Denne metoden sammenligner det rullerende gjennomsnittet for én periode mot en annen forskjøvet
336-
periode, og returnerer en prosentvis endring for hver gruppe. Resultatet inkluderer også en
315+
Denne metoden sammenligner det rullerende gjennomsnittet for én periode mot en annen forskjøvet
316+
periode, og returnerer en prosentvis endring for hver gruppe. Resultatet inkluderer også en
337317
overskrift som beskriver begge periodene.
338318
339319
Args:
@@ -342,10 +322,9 @@ def n_month_rolling_percent_compare(
342322
skip_1 (int, valgfritt): Antall perioder å hoppe over for den sammenlignende perioden. Standard er 1.
343323
344324
Returns:
345-
tuple[str, pl.DataFrame]: En tuple bestående av en sammensatt overskrift og et DataFrame
325+
tuple[str, pl.DataFrame]: En tuple bestående av en sammensatt overskrift og et DataFrame
346326
med prosentvis endring mellom de to periodene for hver gruppe.
347327
"""
348-
349328
header_1, series_1 = self.n_mean_rolling(n, skip)
350329
header_2, series_2 = self.n_mean_rolling(n, skip_1)
351330

@@ -363,8 +342,7 @@ def n_month_rolling_percent_compare(
363342
def n_month_percent_compare(
364343
self, n: int, skip: int = 0, skip_1: int = 1
365344
) -> tuple[str, pl.DataFrame]:
366-
"""
367-
Sammenligner prosentvis endring i verdier mellom to definerte perioder.
345+
"""Sammenligner prosentvis endring i verdier mellom to definerte perioder.
368346
369347
Denne metoden henter to distinkte perioder (hver på `n` måneder), basert på ulike skipverdier,
370348
og beregner den prosentvise forskjellen i verdier mellom dem for hver gruppe i datasettet.
@@ -379,12 +357,11 @@ def n_month_percent_compare(
379357
tuple[str, pl.DataFrame]: En tuple med beskrivelse av periodeparene og et DataFrame
380358
med prosentvis endring mellom verdiene for hver gruppe.
381359
"""
382-
383360
header_1, series_1 = self.n_month(n, skip)
384361
header_2, series_2 = self.n_month(n, skip_1)
385362

386363
header = f"{header_2} / {header_1}"
387-
364+
388365
joined = series_1.join(series_2, on=self._group).select(
389366
**{
390367
self._group: pl.col(self._group),
@@ -395,28 +372,31 @@ def n_month_percent_compare(
395372
)
396373
return header, joined
397374

375+
398376
def rounded_average(df: pd.DataFrame, ordered_columns: list[str]):
399377
df_copy = df[ordered_columns]
400378
df_copy = df_copy.round(1)
401379
res = df_copy.sum(axis="columns").div(len(ordered_columns))
402380
return res.round(1)
403381

382+
404383
def calc_change_rate(df: pd.DataFrame, ordered_columns: list[str], n: int = 1):
405384
results = []
406385
for i in range(n, len(ordered_columns), 1):
407386
col_present = ordered_columns[i]
408-
previous_period = ordered_columns[i-n]
409-
chg_rate = (df[col_present] - df[previous_period])*100/df[previous_period]
387+
previous_period = ordered_columns[i - n]
388+
chg_rate = (df[col_present] - df[previous_period]) * 100 / df[previous_period]
410389
results.append(chg_rate)
411390

412-
return pd.concat(results, axis = "columns", keys = ordered_columns[n:])
391+
return pd.concat(results, axis="columns", keys=ordered_columns[n:])
392+
413393

414394
def rolling_change_rate(df: pd.DataFrame, step: int = 1):
415395
results = []
416396
for i in range(step, len(df.columns), step):
417397
col_present = df.columns[i]
418-
previous_period = df.columns[i-step]
419-
chg_rate = (df[col_present] - df[previous_period])*100/df[previous_period]
398+
previous_period = df.columns[i - step]
399+
chg_rate = (df[col_present] - df[previous_period]) * 100 / df[previous_period]
420400
results.append(chg_rate)
421401

422-
return pd.concat(results, axis = "columns", keys = df.columns[step:])
402+
return pd.concat(results, axis="columns", keys=df.columns[step:])

src/ssb_konjunk/dash/calculations/period_utils.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
import pendulum
55

6-
from from ssb_konjunk.dash.utils import period_parser
7-
86

97
@total_ordering
108
class Period:
@@ -178,4 +176,4 @@ def create_period_range_list(year: int, month: int, n_months: int) -> list[str]:
178176
selected_period = dt.subtract(months=i)
179177
period = period_parser(selected_period.year, selected_period.month)
180178
result.append(period)
181-
return result
179+
return result
Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
21
try:
3-
from .page_aio import Tables, AnalyticsPageAIO # type: ignore
2+
from .page_aio import AnalyticsPageAIO # type: ignore
3+
from .page_aio import Tables # type: ignore
44
except ImportError:
5+
56
class Tables:
67
def __init__(self, *args, **kwargs):
78
raise ImportError(
89
"Tables requires 'dash'. Install it with: pip install ssb-konjunk['dash']"
910
)
10-
11+
1112
class AnalyticsPageAIO:
1213
def __init__(self, *args, **kwargs):
1314
raise ImportError(
1415
"AnalyticsPageAIO requires 'dash'. Install it with: pip install ssb-konjunk['dash']"
1516
)
1617

17-
__all__ = [
18-
"Tables",
19-
"AnalyticsPageAIO"
20-
]
18+
19+
__all__ = ["AnalyticsPageAIO", "Tables"]

0 commit comments

Comments
 (0)