Az intervallum azon valós számok halmaza, amelyek két adott szám mint korlát közé esnek.
Ez eddig valószínűleg nem újdonság. Ha azonban programban modellezni akarjuk, akkor egy kicsit mélyebbre kell tekinteni. Valójában egy intervallum matematikailag egy speciális halmaznak tekinthető, ezért az alapvető halmazműveletek mint az unió, metszet, részintervallum és tartalmazásvizsgálat értelmezhetők. Ugyanakkor az intervallum el is tér a halmazoktól, mert sorba rendezhetők hiszen nagyság szerinti sorrendben meghatározott alsó és felső korláttal rendelkeznek, amelyek az intervallum végpontjai. A számegyenesen való ábrázoláskor az alsó korlát az intervallum értékkészletének bal oldalán, a felső korlát annak jobb oldalán van. Ezért az alsó és felső végpontot, bal illetve jobb oldali végpontnak is nevezik.
Az intervallumot a végpontjai azonban még nem teljesen definiálják. Ugyanis azonos végpontértékekkel rendelkező két intervallum különbözhet egymástól attól függően, hogy a végpontértékeket az intervallum értékkészlete tartalmazza vagy sem. Ha az adott végpontot nem tartalmazza, akkor azon az oldalon az intervallum nyitott, ha tartalmazza akkor zárt. Ennek kombinációi (mindkét oldalon zárt, mindkét oldalon nyitott, bal oldalon zárt és jobb oldalon nyitott, bal oldalon nyitott és jobb oldalon zárt) határozzák meg az intervallum típusát. Ezért egy intervallumot két végpontjának értékei és típusa együttesen definiál.
Ebből következik, hogy az intervallum típusát a műveleteknél figyelembe kell venni. Ez az, ami a halmazokhoz képest összetettebbé teszi az intervallumokkal végzett műveleteket és ezért megvalósításuk némi átgondolást igényel.
Alább egy valós értékű folytonos intervallumot modellező Interval nevű osztály egy lehetséges definíciója látható. A metódusok egyik csoportja az alapvető halmazműveleteket adaptálja, a másik a rendezhetőségből adódó lehetőségeket és igényeket fedi le. Az egyes metódusok szerepét és működésének megértését a dokumentációs karakterláncok segítik.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 |
# modul: interval.py from __future__ import annotations import sys from typing import Literal, Iterator from itertools import pairwise, chain from math import isclose from enum import Enum from collections import namedtuple assert sys.version_info > (3, 9) # Python 3.10+ szükséges. class IntervalType(Enum): """Konstansok a különböző intervallum típusokhoz.""" OPEN = (0, 0) # Mindkét oldalon nyitott. LEFT_OPEN = (0, 1) # Bal oldalon nyitott és jobb oldalon zárt. RIGHT_OPEN = (1, 0) # Jobb oldalon nyitott és bal oldalom zárt. CLOSED = (1, 1) # Mindkét oldalon zárt. IType = IvType = IntervalType IntervalEndpoint = namedtuple('IntervalEndpoint', 'value flag') Endpoints = namedtuple('Endpoints', 'lower upper') class Interval: """Egy valós értékkészletű korlátos intervallumot modellez.""" def __init__(self, lower_endpoint_value: int | float, upper_endpoint_value: int | float, type: IntervalType = IntervalType.CLOSED): """Egy valós értékkészletű intervallumot meghatározza a két végpontja (lower_endpoint_value, upper_endpoint_value), amelyek közül a felső (jobb oldali) nagyobb, mint az alsó (bal oldali), és az, hogy a végeken nyitott vagy zárt. Ez utóbbi az intervallum típusát határozza meg, amelyet a type argumentummal lehet megadni az IntervalType felsorolástípus példányaival. Ezek a lehetséges kombinációkhoz (két oldalon zárt, két oldalon nyitott, bal oldalon nyitott és jobb oldalon zárt, valamint jobb oldalon nyitott és bal oldalon zárt) adnak egy-egy szimbólikus konstanst. Ezek mindegyikének értéke egy kételemű tuple, amely elemek rendre a bal oldal és jobb oldal nyitottságát, illetve zártságát mint bináris információt jelzik. Az érték 0, ha az adott oldalon az intervallum nyitott, és 1, ha zárt. Ezeket, mint a végpontokra jellemző jelzőket az Interval példány flags attribútumában tároljuk el. Ha egy adott végponton zárt az intervallum, akkor a végpont értéke az intervallum értékkészletébe beletartozik, nyitott esetben nem. Műveletek eredményeként kiadódhat olyan elfajult intervallum, amelynek egy eleme van, vagyis az alsó és felső végpontok azonosak. Ez megengedett ebben a modellben. Ellenben az üres, elemet nem tartalmazó intevallum nem értelmezett. Ha egy műveletből ilyen adódna, akkor None lesz az eredmény. """ if lower_endpoint_value > upper_endpoint_value: raise ValueError('Upper endpoint should be greater than lower endpoint.') self._lower_endpoint, self._upper_endpoint = lower_endpoint_value, upper_endpoint_value self._type: IntervalType = type self._flags: tuple = type.value self.endpoints = Endpoints(IntervalEndpoint(lower_endpoint_value, self._flags[0]), IntervalEndpoint(upper_endpoint_value, self._flags[1])) @property def lower_endpoint(self) -> int | float: return self._lower_endpoint @property def upper_endpoint(self) -> int | float: return self._upper_endpoint @property def type(self) -> IntervalType: return self._type @property def flags(self) -> tuple: """Az alsó és felső végpontok nyitott vagy zárt jellegét leíró kételemű tuple. Ha az intervallum az adott végponton nyitott, akkor az érték 0, ha zárt, akkor 1. """ return self._flags def __repr__(self): return '{}({}, {}, {})'.format(type(self).__name__, self.lower_endpoint, self.upper_endpoint, self.type) def __str__(self): brackets = {IntervalType.OPEN: (chr(0x2e28), chr(0x2e29)), IntervalType.LEFT_OPEN: (chr(0x2e28), chr(0x27E7)), IntervalType.RIGHT_OPEN: (chr(0x27E6), chr(0x2e29)), IntervalType.CLOSED: (chr(0x27E6), chr(0x27E7))} return '{}{}, {}{}'.format(brackets[self.type][0], self.lower_endpoint, self.upper_endpoint, brackets[self.type][1]) @classmethod def from_endpoint_values_and_flags(cls, lower_endpoint_value, upper_endpoint_value, lower_endpoint_flag: Literal[0, 1], upper_endpoint_flag: Literal[0, 1]) -> Interval: """Új Interval példányt hoz létre a végpontok, valamint a végpontok nyitottságát vagy zártságát jelző 0 vagy 1 értékek alapján. """ return cls(lower_endpoint_value, upper_endpoint_value, IntervalType((lower_endpoint_flag, upper_endpoint_flag))) @classmethod def from_endpoints(cls, lower_endpoint: IntervalEndpoint, upper_endpoint: IntervalEndpoint) -> Interval: """Új Interval példányt hoz létre a végpontobjektumok alapján.""" return cls.from_endpoint_values_and_flags(lower_endpoint.value, upper_endpoint.value, lower_endpoint.flag, upper_endpoint.flag) @staticmethod def _eq(num1: float, num2: float) -> bool: """Segédfüggvény két valós szám egyenlőségvizsgálatához.""" return isclose(num1, num2, rel_tol=1e-15) def __eq__(self, other) -> bool: """Igaz értékkel tér vissza, ha a self és other egyenlő. Ezek akkor egyenlőek, ha az azonos oldalakon végpontjaik értéke egyenlő, és zártság/nyitottság tekintetében megegyeznek, vagyis értékkészletük azonos. """ if not isinstance(other, Interval): return NotImplemented return self.type == other.type and (self._eq(self.lower_endpoint, other.lower_endpoint) and self._eq(self.upper_endpoint, other.upper_endpoint)) def __hash__(self): return hash((*self.endpoints, *self.flags)) def __lt__(self, other): """Igaz értékkel tér vissza, ha a self kisebb, mint other. A self akkor kisebb, mint other, ha self minden értéke, beleértve a végpontokat is, kisebb az other minden értékénél. """ if not isinstance(other, Interval): return NotImplemented # Ahhoz, hogy a self kisebb legyen, mint other a következő feltételek valamelyikének kell teljesülni. # Ha a self felső végpontja kisebb, mint az other alsó végpontja. cond1 = self.upper_endpoint < other.lower_endpoint # Ha a self felső végpont és az other alsó végpont egyenlő és ezen oldalakon bármelyik intervallum nyitott. cond2 = self._eq(other.lower_endpoint, self.upper_endpoint) and (0 in (other.flags[0], self.flags[1])) return cond1 or cond2 def __le__(self, other) -> bool: """Igaz értékkel tér vissza, ha a self kisebb vagy egyenlő, mint other.""" if not isinstance(other, Interval): return NotImplemented return (self < other) or (self == other) def __contains__(self, value: int | float) -> bool: """Igaz értékkel tér vissza, ha a value a self értékei között szerepel, beleértve a végpontokat is, ha az intervallum azon az oldalon zárt. """ return ((self._eq(self.lower_endpoint, value) * self.flags[0] or self.lower_endpoint < value) and (self._eq(self.upper_endpoint, value) * self.flags[1] or self.upper_endpoint > value)) def __iter__(self) -> Iterator: """Olyan iterátort ad vissza, amely sorban kiadja az alsó és a felső végpontotokat, majd az ezekhez tartozó flagek értékeit.""" return chain((self.lower_endpoint, self.upper_endpoint), self.flags) def length(self) -> int | float: """Visszaadja a végpontok közötti különbséget, azaz az intervallum hosszát, nemnegatív számként.""" return self.upper_endpoint - self.lower_endpoint def midpoint(self) -> float: """Az intervallum középértékét adja vissza.""" return (self.lower_endpoint + self.upper_endpoint) / 2 def reprval(self, key: Literal['lower', 'upper', 'mid'] = 'lower') -> int | float: """Visszaadja az alsó és felső végpont, valamint középpont közül a key argumentummal kiválasztott értéket, amely az intervallumot fogja képviselni. """ repr_values = {'lower': self.lower_endpoint, 'upper': self.upper_endpoint, 'mid': self.midpoint()} return repr_values[key] def split(self, n: int) -> Iterator[Interval]: """Az intervallumot n részintervallumra osztja. Ahhoz, hogy egy keresett érték csak egyetlen részintervallumban legyen megtalálható, minden részintervallum alsó végpontja megegyezik a felosztott intervallum alsó végpontjával, a felső végpontok ehhez igazodnak, hogy a csatlakozó végpontok zártsága/nyitottsága ellentétes legyen. Az utolsó részintervallum felső végpontja a felosztott intervallum felső végpontjának megfelelő lesz. """ width = self.length() / n # Az első (n-1) darab részintervallum előállítása. iterable1 = (Interval.from_endpoint_values_and_flags(a, b, self.flags[0], self.flags[0] ^ 1) for a, b in pairwise(self.lower_endpoint + width * i for i in range(n))) # Az utolsó részintervallum előállítása. iterable2 = [Interval.from_endpoint_values_and_flags(left_point := self.lower_endpoint + width * (n - 1), left_point + width, self.flags[0], self.flags[1])] return chain(iterable1, iterable2) def sort(self, *others: Interval) -> list[Interval]: """Egy listával tér vissza, amelynek a self és az others argumentummal megadott intervallumok az elemei az alsó végpontjaik szerint növekvő sorrendben. """ return sorted((self, *others), key=lambda _iv: _iv.lower_endpoint) def __and__(self, other: Interval) -> Interval | None: """Olyan új Interval példánnyal tér vissza, amely a self és other közös részét (metszetét) képviseli. Két intervallum közös része egy olyan intervallum, amelyben legalább egy olyan érték van, amely közös a két intervallumban. """ _iv1, _iv2 = self.sort(other) if t := _iv1.adj_iv_common_endpoint(_iv2): _, flag1, flag2 = t if flag1 & flag2: return Interval.from_endpoints(_iv1.endpoints, _iv2.endpoints) else: return None elif not (_iv2 > _iv1): intersect_lower_value = _iv2.lower_endpoint if self._eq(_iv1.lower_endpoint, _iv2.lower_endpoint): intersect_lower_flag = _iv2.endpoints.lower.flag & _iv1.endpoints.lower.flag else: intersect_lower_flag = _iv2.endpoints.lower.flag intersect_upper_endpoint: IntervalEndpoint = min((_iv1.endpoints.upper, _iv2.endpoints.upper), key=lambda ep: ep.value) if self._eq(_iv1.upper_endpoint, _iv2.upper_endpoint): intersect_upper_flag = _iv2.endpoints.upper.flag & _iv1.endpoints.upper.flag else: intersect_upper_flag = intersect_upper_endpoint.flag return Interval.from_endpoint_values_and_flags(intersect_lower_value, intersect_upper_endpoint.value, intersect_lower_flag, intersect_upper_flag) return None def __or__(self, other: Interval) -> Interval | None: """Olyan új Interval példánnyal tér vissza, amely a self és other egyesítésével jön létre. Az egyesített intervallumban minden érték szerepel, amely legalább az egyik összetevőnek eleme. Ha ez a feltétel nem teljesül, akkor a self és other nem egyesíthető egyetlen intervallummá. Ekkor a visszatérési érték None. """ _iv1, _iv2 = self.sort(other) if t := _iv1.adj_iv_common_endpoint(_iv2): _, flag1, flag2 = t if flag1 | flag2: return Interval.from_endpoints(_iv1.endpoints, _iv2.endpoints) else: return None elif not (_iv2 > _iv1): union_lower_value = _iv1.lower_endpoint if self._eq(_iv1.lower_endpoint, _iv2.lower_endpoint): union_lower_flag = _iv1.endpoints.lower.flag | _iv2.endpoints.lower.flag else: union_lower_flag = _iv1.endpoints.lower.flag union_upper_endpoint: IntervalEndpoint = max((_iv1.endpoints.upper, _iv2.endpoints.upper), key=lambda ep: ep.value) if self._eq(_iv1.upper_endpoint, _iv2.upper_endpoint): union_upper_flag = _iv1.endpoints.upper.flag | _iv2.endpoints.upper.flag else: union_upper_flag = union_upper_endpoint.flag return Interval.from_endpoint_values_and_flags(union_lower_value, union_upper_endpoint.value, union_lower_flag, union_upper_flag) return None def __add__(self, value: int | float) -> Interval: """Olyan új Interval példánnyal tér vissza, amely a végein ugyanúgy zárt/nyitott, mint self, de két végpontja a self végpontjaihoz képest value értékkel növeltek. Más szóval, az új intervallum a self-hez képest value értékkel el van tolva. """ return type(self)(self.lower_endpoint + value, self.upper_endpoint + value, self.type) def __radd__(self, value: int | float) -> Interval: return self + value def __mul__(self, value: int | float) -> Interval: """Olyan új Interval példánnyal tér vissza, amely a végein ugyanúgy zárt/nyitott, mint self és középpontja megegyezik a self középpontjával, de szélessége value-szerese a self szélességének, ahol value > 0 valós szám. """ if value <= 0: raise ValueError('Az argumentum pozítv valós szám kell, hogy legyen.') return type(self)(self.midpoint() - (self.midpoint() - self.lower_endpoint) * value, self.midpoint() + (self.upper_endpoint - self.midpoint()) * value, self.type) def __rmul__(self, value: int | float) -> Interval: return self * value def adj_iv_common_endpoint(self, other: Interval) -> tuple: """Ha két intervallum szomszédos, akkor egy tuple-ban visszaadja a közös végpont értékét, valamint e point zártságát jelző flag értékeket az alsó végpontok szerint sorbarendezett intervallumok sorrendjében. A visszatérési érték egy üres tuple, ha a két intervallum nem szomszédos. Pl.: Ha iv1 = Interval(2, 5, IntervalType.CLOSED), iv2 = Interval(-1, 2, IntervalType.RIGHT_OPEN), akkor iv1.common_endpoint_and_flags(iv2) == (2, 0, 1) """ if self.is_adjacent_to(other): _iv1, _iv2 = self.sort(other) return _iv1.upper_endpoint, _iv1.endpoints.upper.flag, _iv2.endpoints.lower.flag return () def union(self, other: Interval) -> Interval | None: """Olyan új Interval példánnyal tér vissza, amely a self és other egyesítésével jön létre. Hatásában megegyezik a | operátoréval. """ return self | other def intersection(self, other: Interval) -> Interval | None: """Olyan új Interval példánnyal tér vissza, amely a self és other közös részét (metszetét) képviseli. Hatásában megegyezik a & operátoréval. """ return self & other def overlaps(self, other: Interval) -> bool: """Igaz értéket ad vissza, ha a self és other átlapolódó intervallumok. Két intervallum átlapolódik, ha legalább egy közös értékük van. """ return bool(self & other) def is_subinterval(self, other: Interval) -> bool: """A self akkor részintervalluma az other intervallumnak, ha minden értéke other-nek is értéke. Ezért az egyenlő intervallumok is egymás részintervallumai. """ # A self nem részintervallum, ha az alábbiek közül bármelyik igaz. # Az azonos oldali végpontok egyenlőek, de other az adott végponton nyitott. cond1 = self._eq(self.lower_endpoint, other.lower_endpoint) and (self.flags[0] == 1 and other.flags[0] == 0) cond2 = self._eq(self.upper_endpoint, other.upper_endpoint) and (self.flags[1] == 1 and other.flags[1] == 0) # A self alsó (felső) végpontja kisebb (nagyobb), mint az other alsó (felső) végpontja. cond3 = self.lower_endpoint < other.lower_endpoint cond4 = self.upper_endpoint > other.upper_endpoint return not (cond1 or cond2 or cond3 or cond4) def is_adjacent_to(self, other: Interval) -> bool: """Két intervallumot szomszédosnak tekintünk, ha a hosszuk összege megegyezik a legkisebb és legnagyobb végpotok által alkotott intervallum hosszával. Ha ez teljesül, akkor True értékkel tér vissza függetlenül attól, hogy az egyenlő értékű végpontok zártsága milyen (mindkét végpont zárt, csak valamelyik, vagy egyik sem). """ _iv1, _iv2 = self.sort(other) return self._eq(_iv1.length() + _iv2.length(), Interval(_iv1.lower_endpoint, _iv2.upper_endpoint).length()) def is_closed(self) -> bool: """Igaz értékkel tér vissza, ha az intervallum mindkét végpontján zárt.""" return self.type == IntervalType.CLOSED def is_left_open(self) -> bool: """Igaz értékkel tér vissza, ha az intervallum alsó végpontján nyitott, felső végpontján zárt.""" return self.type == IntervalType.LEFT_OPEN def is_right_open(self) -> bool: """Igaz értékkel tér vissza, ha az intervallum alsó végpontján zárt, felső végpontján nyitott.""" return self.type == IntervalType.RIGHT_OPEN def is_open(self) -> bool: """Igaz értékkel tér vissza, ha az intervallum mindkét végpontján nyitott.""" return self.type == IntervalType.OPEN def is_half_open(self) -> bool: """Igaz értékkel tér vissza, ha az intervallum egyik végpontján nyitott, a másikon zárt.""" return self.type in (IntervalType.RIGHT_OPEN, IntervalType.LEFT_OPEN) |
Alkalmazási lehetőségek és példák
Az Interval osztály, illetve példányai minden olyan feladatnál és alkalmazásnál hasznosak lehetnek, ahol intervallumok merülnek fel, vagy konkrétan azokkal kell dolgozni. Többek között ilyenek az intervallumfelezést használó eljárások, vagy statisztikai alkalmazások mint például relatív gyakoriságok számítása, hisztogramok készítése, vagy osztályközös gyakorisági sorok statisztikai jellemzőinek (pl. átlag, medián) számítása.
A következőkben az említett három felhasználási esetre láthatunk példát.
Elsőként mutatjuk a négyzetgyökvonás intervallumfelezéssel való elvégzését. Ez nem gyakorlati alkalmazásra készült, hanem inkább csak azt szemlélteti, hogy a felezéses eljárás hogyan valósítható meg az Interval példányokat használva.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
from interval import Interval from math import isclose def sqrt(x) -> float: """Az x négyzetgyökével tér vissza. A számítás intervallumfelezéses eljárással történik.""" if x < 0: raise ValueError('Az argumentum nem lehet negatív szám.') current_interval = Interval(0, 1 if (0 <= x < 1) else x) while not isclose((middle_point := current_interval.midpoint()) ** 2, x, rel_tol=1e-15): lower_half, upper_half = current_interval.split(2) if middle_point ** 2 > x: current_interval = lower_half else: current_interval = upper_half return middle_point # TESZT print('Négyzetgyök 2 =', sqrt(2)) # Négyzetgyök 2 = 1.414213562373095 print('Négyzetgyök 0.75 =', sqrt(0.75)) # Négyzetgyök 0.75 = 0.8660254037844384 # Egész értékek négyzetgyökének pontosságvizsgálata. for n in range(10000): if abs(sqrt(n) - pow(n, 0.5)) > 1e-13: print(n, y := sqrt(n), sqrt2 := pow(n, 0.5), abs(y - sqrt2)) # Eredmény: A megadott értéktartományban és abszolút hibahatáron belül egyezik a pow() függvénnyel számítottal. # Egynél kisebb értékek négyzetgyökének pontosságvizsgálata. s = 0.0001 for n in range(10000): if abs(sqrt(n * s) - pow(n * s, 0.5)) > 1e-15: print(n * s, y := sqrt(n*s), sqrt2 := pow(n * s, 0.5), abs(y - sqrt2)) # Eredmény: A megadott értéktartományban és abszolút hibahatáron belül egyezik a pow() függvénnyel számítottal. |
A következő két alkalmazás több gyakorlati hasznossággal bír.
Az alábbi egy egyszerű megvalósítású relatív gyakoriságot számoló függvényt mutat:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
from interval import Interval from typing import Iterable, Literal from random import triangular from matplotlib import pyplot as plt def relative_frequency(data: Iterable[int | float], a: int | float, b: int | float, bins: int = 50, bin_repr_value: Literal['lower', 'upper', 'mid'] = 'lower'): """A data által kiadott és az [a, b] intervallumba eső valós számok relatív gyakoriságát adja vissza egy szótárban, amelynek kulcsai az [a, b] intervallum bins számú részintervallumainak az alsó vagy felső végpontja, illetve az intervallum középértéke attól függően, hogy a bin_repr_value milyen értékre van állítva. """ frequencies = dict.fromkeys(Interval(a, b).split(bins), 0) total_data_points = 0 for total_data_points, x in enumerate(data, 1): for interval in frequencies.keys(): if x in interval: frequencies[interval] += 1 if total_data_points: return {iv.reprval(bin_repr_value): freq / total_data_points for iv, freq in frequencies.items()} raise ValueError('A "data" argumentum legalább egy elemet kell, hogy kiadjon.') # TESZT # Háromszögeloszlású véletlen értékeket generálunk adott módusszal. rand_seq = (triangular(-100, 100, 50) for _ in range(100000)) # Előállítjuk a relatív gyakoriságukat és ezt ábrázolva megnézzük, hogy # mennyire közelíti a háromszögeloszlást. rel_freq = relative_frequency(rand_seq, -100, 100) # A relatív gyakoriság ábrázolása. xs = rel_freq.keys() ys = rel_freq.values() plt.plot(xs, ys, '.') plt.title('Háromszögeloszlás') plt.legend(loc='upper left', labels=['triangular(-100, 100, 50)']) plt.xlabel('Véletlen változó értéke') plt.ylabel('Relatív gyakoriság') plt.text(60, 0.038, "módusz = 50") plt.show() |
A kapott értékeket ábrázolva vizuálisan is ellenőrizhetjük, hogy háromszögeloszlásról van szó.

A harmadik alkalmazási példában a magyarországi nyugdíjak és egyéb ellátások átlagát és mediánját becsüljük meg a KSH adatsorai és egyéb nyilvános információk alapján, viszonylag egyszerű módon, az Interval példányokat és metódusaikat is használva. Az eredmények jó közelítéssel egyeznek a sajtóban közölt értékekkel.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
from interval import Interval, IntervalType from collections import Counter from itertools import repeat, chain from statistics import median, mean # Saját jogon járó nyugdíjban és ellátásban részesülők a teljes ellátás összege szerint, 2024 január. # Osztályközök (nyugdíjintervallumok) és a gyakoriságok (hány fő esik az intervallumba). pension_groups1 = Interval(0, 40_000, IntervalType.OPEN).split(1) pension_groups2 = Interval(40_000, 500_000, IntervalType.RIGHT_OPEN).split(23) pension_groups3 = Interval(500_000, 600_000, IntervalType.RIGHT_OPEN).split(1) pension_groups4 = Interval(600_000, 3000_000, IntervalType.CLOSED).split(1) frequencies = (20_523, 40_322, 68_253, 68_829, 116_958, 188_143, 208_540, 241_008, 213_268, 184_412, 160_182, 130_811, 107_759, 91_153, 76_771, 64_222, 52_959, 42_793, 34_816, 28_423, 23_472, 19_135, 15_432, 12_790, 35_912, 24_475) print('''Saját jogon járó nyugdíjban és ellátásban részesülők (fő) a teljes ellátás összege szerint (Ft), 2024 január.''') # Az egymást követő osztályközök összefűzése. pension_groups = chain(pension_groups1, pension_groups2, pension_groups3, pension_groups4) # Osztályközös gyakoriságtábla elkészítése. grouped_frequency_table = Counter(dict(zip((iv for iv in pension_groups), frequencies))) # A nyugdíjintervallumok és ezekbe eső ellátásban részedülők számának kiírása. print(*(f'{str(pension_group):21}: {head_count:>7}' for pension_group, head_count in sorted(grouped_frequency_table.most_common())), sep='\n') # Az osztályközöket képviselő középértékek és gyakoriságok megfeleltetése. midpoint_frequency_table = Counter({iv.reprval('mid'): fr for iv, fr in grouped_frequency_table.items()}) print('Becsült átlag nyugdíj és ellátás: {0:_g} Ft'.format(mean(midpoint_frequency_table.elements()))) print('Becsült medián nyugdíj és ellátás: {0:_g} Ft'.format(median(midpoint_frequency_table.elements()))) # Források: # https://www.ksh.hu/stadat_files/szo/hu/szo0035.html # https://www.ksh.hu/s/helyzetkep-2023/#/kiadvany/nyugdijak-es-egyeb-ellatasok/ # sajat-jogon-jaro-nyugdijban-es-ellatasban-reszesulok-szama-nemek-es-a-teljes-ellatas-osszege-szerint-2024-januar # https://forbes.hu/penz/nyugdij-emeles-reform-2024/ |
A forráskódok a https://github.com/pythontudasepites/real_interval linken elérhetők.
Az Interval osztály definálásánál és az alkalmazási példákban hangsúlyosan kerülnek elő a speciális konténerek mint például a namedtuple és Counter, a felsorolás típus, valamint a véletlen számok és a statisztikai jellemzők számítása. Ezekkel a Python tudásépítés lépésről lépésre című e-könyvben részletesen a „Különleges osztálydefiníciók” fejezet „Felsorolástípus” alfejezete, a „Készétel fogyasztás – a szabványos könyvtár moduljainak használata” fejezet „Speciális konténer típusok”, „A véletlen használatba vétele” és a „Statisztikai eszköztár” című alfejezete foglalkoznak.