*Kai dangus prabyla: žaibų dinamika ir ką jie pasako apie lietų ⛈️*


*Įvadas*

Vyraujančios meteorologinės sąlygos, o kartu ir įvairūs meteorologiniai reiškiniai yra neasiejama mūsų gyvenimo dalis. Greitai, o gal ir neplanuotai besikeičiantys orai, juos lydintys pavojingi meteorologiniai reiškinai gali pakoreguoti ne tik mūsų gyvenimo rutiną, bet ir artimiausių dienų planus.

Žvelgiant į besikeičiančių orų dinamiką svarbu įvertinti ne tik vieno, tuo metu vyraujančio meteorologinio reišknio, tačiau ir tuo pat metu susidarančių kitų reikšnių sąsajas. Šiame darbe bus apžvelgiamas žaibų išlydžių Lietuvos teritorije pasiskirstymas ir tarpusavio ryšiai su iškrentančiu kritulių kiekiu 2023 – 2025 m.

No description has been provided for this image

*Tikslas*

Išanalizuoti žaibų pasisikirtymą Lietuvos teritorije ir įvertinti jų sąsajas su iškrentančiu kritulių kiekiu.

*Hipotezės*

Žaibų pasiskirstymas Lietuvoje nėra visiškai tolygus – skirtingais metais išryškėja tam tikri teritoriniai ir pasiskirtymo laike skirtumai.

Stebint didelį žaibų išlydžių kiekį, fikuojamas ir gausesnis kritulių kiekis.

Žaibų išlydžių pasiskirstymas sutampa su gausaus kritulių kiekio zonomis.

Žaibų išlydžių kiekis yra glaudžiai susijęs su trumpalaikiais, intensyviais kritulių epizodais.

*Kintamieji*

Obstime, dg_obstime – laikas

dg_name, vardas – savivaldybės

rr_per – kritulių kiekis


In [1]:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
In [2]:
df_2023 = pd.read_csv(r'C:\Users\ievam\Desktop\Duomenu analitika\Baigiamasis darbas\Python dalis\p.krituliai_2023.csv')  
In [3]:
df_2024 = pd.read_csv(r'C:\Users\ievam\Desktop\Duomenu analitika\Baigiamasis darbas\Python dalis\p.krituliai_2024.csv')
In [4]:
df_2025 = pd.read_csv(r'C:\Users\ievam\Desktop\Duomenu analitika\Baigiamasis darbas\Python dalis\p.krituliai_2025.csv')
In [5]:
df_krituliai = pd.concat([df_2023, df_2024, df_2025], ignore_index=True)
In [6]:
df_zaibai = pd.read_csv(r'C:\Users\ievam\Desktop\Duomenu analitika\Baigiamasis darbas\Python dalis\p.zaibai_2023_2025.csv')  
In [7]:
df_zaibai['kiekis'] = 1 

Visų savivaldybių kritulių suma:

In [8]:
df_krituliai['dg_obstime'] = pd.to_datetime(df_krituliai['dg_obstime'])

df_krituliai['rr_per'] = pd.to_numeric(df_krituliai['rr_per'], errors='coerce')

hourly_sum_krituliai = df_krituliai.groupby('dg_obstime')['rr_per'].sum().reset_index()

print(hourly_sum_krituliai.head(10))
           dg_obstime  rr_per
0 2023-01-01 00:00:00    25.4
1 2023-01-01 01:00:00    28.9
2 2023-01-01 02:00:00    33.6
3 2023-01-01 03:00:00    30.8
4 2023-01-01 04:00:00    31.5
5 2023-01-01 05:00:00    30.8
6 2023-01-01 06:00:00    32.8
7 2023-01-01 07:00:00    17.8
8 2023-01-01 08:00:00    10.2
9 2023-01-01 09:00:00     8.9

Visų savivaldybių žaibų suma:

In [9]:
df_zaibai['obstime'] = pd.to_datetime(df_zaibai['obstime'], errors='coerce')

df_zaibai['hour'] = df_zaibai['obstime'].dt.floor('h')

hourly_sum = df_zaibai.groupby('hour')['kiekis'].sum()

full_range = pd.date_range(start=df_zaibai['hour'].min(), end=df_zaibai['hour'].max(), freq='h')

hourly_sum_zaibai = hourly_sum.reindex(full_range, fill_value=0)

hourly_sum_zaibai = hourly_sum_zaibai.reset_index().rename(columns={'index': 'laikas', 'kiekis': 'žaibų kiekis'})

print(hourly_sum_zaibai.head(10))
               laikas  žaibų kiekis
0 2023-03-07 19:00:00             6
1 2023-03-07 20:00:00             4
2 2023-03-07 21:00:00             3
3 2023-03-07 22:00:00             8
4 2023-03-07 23:00:00             1
5 2023-03-08 00:00:00             2
6 2023-03-08 01:00:00             0
7 2023-03-08 02:00:00             0
8 2023-03-08 03:00:00             0
9 2023-03-08 04:00:00             0

Visų savivaldybių žaibų ir kritulių kiekio pasiskirstymas skirtingais metais:

In [10]:
hourly_sum_krituliai = hourly_sum_krituliai.rename(columns={'dg_obstime': 'time', 'rr_per': 'krituliai'})
hourly_sum_zaibai = hourly_sum_zaibai.rename(columns={'laikas': 'time', 'žaibų kiekis': 'zaibai'})

years = [2023, 2024, 2025]
max_rain = 220
max_lightning = 4500

for year in years:
    rain_year = hourly_sum_krituliai[hourly_sum_krituliai['time'].dt.year == year]
    lightning_year = hourly_sum_zaibai[hourly_sum_zaibai['time'].dt.year == year]
    
    fig, ax1 = plt.subplots(figsize=(12,5))

    ax1.bar(rain_year['time'], rain_year['krituliai'], color='#00b3b3', label='Krituliai', width=0.2)
    ax1.set_ylabel('Krituliai (mm)', color='#00b3b3')
    ax1.set_ylim(0, max_rain)
    ax1.tick_params(axis='y', labelcolor='#00b3b3')

    if not rain_year.empty:
        ax1.set_xlim(rain_year['time'].min(), rain_year['time'].max())

    ax2 = ax1.twinx()
    ax2.fill_between(lightning_year['time'], 0, lightning_year['zaibai'], color='orange', alpha=0.2)
    ax2.plot(lightning_year['time'], lightning_year['zaibai'], label='Žaibai', color='orange',) 
    ax2.set_ylabel('Žaibų kiekis', color='orange',)
    ax2.set_ylim(0, max_lightning)
    ax2.tick_params(axis='y', labelcolor='orange',)

    plt.title(f'Krituliai ir žaibai {year} metais')
    fig.tight_layout()
    
    lines, labels = ax1.get_legend_handles_labels()
    lines2, labels2 = ax2.get_legend_handles_labels()
    ax1.legend(lines + lines2, labels + labels2, loc='upper left')

    plt.show()
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image

Daugiausiai kritulių per visą laikotarpį iškrito:

In [11]:
df_krituliai['dg_obstime'] = pd.to_datetime(df_krituliai['dg_obstime'])

sum_by_city = (
    df_krituliai[df_krituliai['dg_obstime'].dt.year.between(2023, 2025)]
    .groupby('dg_name')['rr_per'].sum())

max_city, max_value = sum_by_city.idxmax(), sum_by_city.max()

print(f"Daugiausia kritulių (2023–2025 m.) iškrito savivaldybėje: {max_city}")
print(f"Bendras kritulių kiekis: {max_value:.2f} mm")
Daugiausia kritulių (2023–2025 m.) iškrito savivaldybėje: Plungės AMS
Bendras kritulių kiekis: 2627.30 mm
In [12]:
df_krituliai['dg_obstime'] = pd.to_datetime(df_krituliai['dg_obstime'])
df_zaibai['obstime'] = pd.to_datetime(df_zaibai['obstime'])

rain = df_krituliai[df_krituliai['dg_name'] == 'Plungės AMS'].copy()
rain = rain.groupby(rain['dg_obstime'].dt.floor('h'))['rr_per'].sum()

lightning = df_zaibai[df_zaibai['vardas'] == 'Plungės r. sav.'].copy()
lightning = lightning.groupby(lightning['obstime'].dt.floor('h'))['kiekis'].sum()

all_hours = rain.index.union(lightning.index)

fig, ax1 = plt.subplots(figsize=(15,5))
bar_width = 0.6
offset = pd.Timedelta(minutes=15)

ax1.bar(all_hours - offset, [rain.get(h,0) for h in all_hours], width=bar_width, color='#006064', label='Krituliai')
ax2 = ax1.twinx()
ax2.bar(all_hours + offset, [lightning.get(h,0) for h in all_hours], width=bar_width, color='#FF6F00', label='Žaibai')

ax1.set_xlabel('Laikas')
ax1.set_ylabel('Krituliai (mm)', color='#006064')
ax2.set_ylabel('Žaibų kiekis', color='#FF6F00')
ax1.tick_params(axis='y', labelcolor='#006064')
ax2.tick_params(axis='y', labelcolor='#FF6F00')

ax1.set_xlim(pd.to_datetime('2023-01-01'), pd.to_datetime('2025-09-30'))

plt.title('Plungės AMS krituliai ir Plungės r. sav. žaibai')
fig.tight_layout()
plt.show()
No description has been provided for this image

Daugiausiai žaibų išlydžių per visą laikotarpį buvo:

In [13]:
df_zaibai['obstime'] = pd.to_datetime(df_zaibai['obstime'])

df_zaibai['hour'] = df_zaibai['obstime'].dt.floor('h')

hourly_sum = df_zaibai.groupby(['vardas', 'hour'])['kiekis'].sum().reset_index()

total_sum = hourly_sum.groupby('vardas')['kiekis'].sum().reset_index()

total_sum = total_sum.sort_values('kiekis', ascending=False)

max_city = total_sum.iloc[0]['vardas']
max_value = total_sum.iloc[0]['kiekis']

print(f"Daugiausia žaibų buvo savivaldybėje: {max_city}")
print(f"Bendras žaibų kiekis: {int(max_value)}")
Daugiausia žaibų buvo savivaldybėje: Panevėžio r. sav.
Bendras žaibų kiekis: 13590
In [14]:
df_krituliai['dg_obstime'] = pd.to_datetime(df_krituliai['dg_obstime'])
df_zaibai['obstime'] = pd.to_datetime(df_zaibai['obstime'])

rain = df_krituliai[df_krituliai['dg_name'] == 'Panevėžio AMS'].copy()
rain = rain.groupby(rain['dg_obstime'].dt.floor('h'))['rr_per'].sum()

lightning = df_zaibai[df_zaibai['vardas'] == 'Panevėžio r. sav.'].copy()
lightning = lightning.groupby(lightning['obstime'].dt.floor('h'))['kiekis'].sum()

all_hours = rain.index.union(lightning.index)

fig, ax1 = plt.subplots(figsize=(15,5))
bar_width = 0.6
offset = pd.Timedelta(minutes=15)

ax1.bar(all_hours - offset, [rain.get(h,0) for h in all_hours], width=bar_width, color='#006064', label='Krituliai')
ax2 = ax1.twinx()
ax2.bar(all_hours + offset, [lightning.get(h,0) for h in all_hours], width=bar_width, color='#FF6F00', label='Žaibai')

ax1.set_xlabel('Laikas')
ax1.set_ylabel('Krituliai (mm)', color='#006064')
ax2.set_ylabel('Žaibų kiekis', color='#FF6F00')
ax1.tick_params(axis='y', labelcolor='#006064')
ax2.tick_params(axis='y', labelcolor='#FF6F00')

ax1.set_xlim(pd.to_datetime('2023-01-01'), pd.to_datetime('2025-09-30'))

plt.title('Panevėžio AMS krituliai ir Panevėžio r. sav. žaibai')
fig.tight_layout()
plt.show()
No description has been provided for this image

10 meteorologijos stočių, kuriose 1 val. iškrito daugiausiai kritulių:

In [15]:
df_period = df_krituliai[df_krituliai['dg_obstime'].dt.year.between(2023, 2025)]

top10 = df_period.nlargest(10, 'rr_per').copy()

df_top10 = top10[['dg_name', 'dg_obstime', 'rr_per']].copy()
df_top10.columns = ['Stotis', 'Laikas', 'Kritulių kiekis (mm)']

df_top10.reset_index(drop=True, inplace=True)
display(df_top10)
Stotis Laikas Kritulių kiekis (mm)
0 Klaipėdos AMS 2023-08-06 05:00:00 53.5
1 Skuodo AMS 2024-05-25 17:00:00 45.4
2 Varėnos AMS 2023-08-05 19:00:00 36.2
3 Laukuvos AMS 2024-08-18 22:00:00 35.9
4 Pakruojo AMS 2023-08-18 00:00:00 35.3
5 Varėnos AMS 2025-09-03 16:00:00 33.7
6 Šakių AMS 2025-07-11 00:00:00 33.1
7 Rietavo AMS 2025-07-28 15:00:00 32.4
8 Nidos AMS 2025-09-14 16:00:00 32.4
9 Dūkšto AMS 2023-08-05 20:00:00 32.3
In [16]:
station_to_savivaldybe = {'Klaipėdos AMS': 'Klaipėdos r. sav.','Skuodo AMS': 'Skuodo r. sav.','Varėnos AMS': 'Varėnos r. sav.',
                          'Laukuvos AMS': 'Šilalės r. sav.','Pakruojo AMS': 'Pakruojo r. sav.','Šakių AMS': 'Šakių r. sav.',
                          'Rietavo AMS': 'Rietavo sav.', 'Nidos AMS': 'Neringos sav.', 'Dūkšto AMS': 'Ignalinos r. sav.',}

top10_cases = df_top10[['Stotis', 'Laikas']].copy()
top10_cases['date'] = top10_cases['Laikas'].dt.date.astype(str)
top10_cases['savivaldybe'] = top10_cases['Stotis'].map(station_to_savivaldybe)

fig, axes = plt.subplots(5, 2, figsize=(15, 14), sharex=True)
axes = axes.flatten()

rain_color = '#1f77b4'
lightning_color = '#ff7f0e'

for idx, case in enumerate(top10_cases.itertuples()):
    station = case.Stotis
    date = case.date
    savivaldybe = case.savivaldybe

    ax1 = axes[idx]
    ax2 = ax1.twinx()

    df_rain = df_krituliai[
        (df_krituliai['dg_name'] == station) &
        (df_krituliai['dg_obstime'].dt.date.astype(str) == date)
    ]
    rain_hourly = df_rain.groupby(df_rain['dg_obstime'].dt.hour)['rr_per'] \
                         .sum() \
                         .reindex(range(24), fill_value=0)

    df_lightning = df_zaibai[
        (df_zaibai['vardas'] == savivaldybe) &
        (df_zaibai['obstime'].dt.date.astype(str) == date)
    ].copy()
    df_lightning['kiekis'] = df_lightning['kiekis'].clip(lower=0)
    zaibai_hourly = df_lightning.groupby(df_lightning['obstime'].dt.hour)['kiekis'] \
                                .sum() \
                                .reindex(range(24), fill_value=0)

    hours = range(24)

    ax1.plot(hours, rain_hourly.values, color=rain_color, linewidth=2, label='Krituliai (mm)')
    ax2.plot(hours, zaibai_hourly.values, color=lightning_color, linestyle='--', linewidth=2, label='Žaibai (vnt)')

    ax1.set_title(f"{date} {station} / {savivaldybe}", fontsize=11)
    ax1.set_xticks(range(0, 24, 2))
    ax1.set_ylim(bottom=0)
    ax2.set_ylim(bottom=0)
    ax1.set_ylabel('Krituliai (mm)', color=rain_color, fontsize=10)
    ax2.set_ylabel('Žaibai (vnt)', color=lightning_color, fontsize=10)

    lines1, labels1 = ax1.get_legend_handles_labels()
    lines2, labels2 = ax2.get_legend_handles_labels()
    ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper right', fontsize=9)

plt.xlabel('Valanda')
plt.suptitle('10 MS, kuriose per 1 val. iškrito daugiausiai kritulių ir tos dienos žaibų išlydžių pasiskirstymas', fontsize=14)
plt.tight_layout(rect=[0, 0, 1, 0.96])
plt.show()
No description has been provided for this image

10 savivaldybių, kuriose per 1 val. fiksuotas didžiausias žaibų kiekis:

In [17]:
df_zaibai['obstime'] = pd.to_datetime(df_zaibai['obstime'])

df_period = df_zaibai[(df_zaibai['obstime'].dt.year.between(2023, 2025)) & (df_zaibai['obstime'] < '2025-09-30')].copy()

df_period['valanda'] = df_period['obstime'].dt.floor('h')

df_sum = (df_period.groupby(['vardas', 'valanda'], as_index=False)['kiekis'].sum())

df_zaibai_top10 = df_sum.nlargest(10, 'kiekis')
df_zaibai_top10.reset_index(drop=True, inplace=True)
df_zaibai_top10
Out[17]:
vardas valanda kiekis
0 Panevėžio r. sav. 2023-08-07 08:00:00 1167
1 Kėdainių r. sav. 2024-07-11 13:00:00 1000
2 Joniškio r. sav. 2023-08-07 08:00:00 936
3 Zarasų r. sav. 2023-08-30 14:00:00 927
4 Vilkaviškio r. sav. 2023-08-06 19:00:00 911
5 Jurbarko r. sav. 2023-08-30 00:00:00 851
6 Vilkaviškio r. sav. 2023-08-30 15:00:00 839
7 Panevėžio r. sav. 2024-07-13 15:00:00 827
8 Molėtų r. sav. 2024-07-13 09:00:00 817
9 Joniškio r. sav. 2023-06-19 14:00:00 805
In [26]:
savivaldybe_to_station = {'Panevėžio r. sav.': 'Panevėžio AMS','Kėdainių r. sav.': 'Dotnuvos AMS','Joniškio r. sav.': 'Joniškio AMS',
                        'Zarasų r. sav.': 'Zarasų AMS','Vilkaviškio r. sav.': 'Kybartų AMS','Jurbarko r. sav.': 'Jurbarko AMS',
                        'Molėtų r. sav.': 'Molėtų AMS'}

df_zaibai_top10['date'] = df_zaibai_top10['valanda'].dt.date.astype(str)
df_zaibai_top10['hour'] = df_zaibai_top10['valanda'].dt.hour

df_zaibai_top10['station'] = df_zaibai_top10['vardas'].map(savivaldybe_to_station)

fig, axes = plt.subplots(5, 2, figsize=(15, 14), sharex=True)
axes = axes.flatten()

rain_color = '#1f77b4'
lightning_color = '#ff7f0e'

for idx, row in df_zaibai_top10.iterrows():
    sav = row['vardas']
    station = row['station']
    date = row['date']

    ax1 = axes[idx]
    ax2 = ax1.twinx()

   
    df_rain = df_krituliai[
        (df_krituliai['dg_name'] == station) &
        (df_krituliai['dg_obstime'].dt.date.astype(str) == date)
    ]

    rain_hourly = df_rain.groupby(df_rain['dg_obstime'].dt.hour)['rr_per'] \
                         .sum() \
                         .reindex(range(24), fill_value=0)

    
    df_lightning = df_zaibai[
        (df_zaibai['vardas'] == sav) &
        (df_zaibai['obstime'].dt.date.astype(str) == date)
    ].copy()

    df_lightning['kiekis'] = df_lightning['kiekis'].clip(lower=0)

    zaibai_hourly = df_lightning.groupby(df_lightning['obstime'].dt.hour)['kiekis'] \
                                .sum() \
                                .reindex(range(24), fill_value=0)

    hours = range(24)

    ax1.bar(hours, rain_hourly.values, color=rain_color, label='Krituliai (mm)')
    ax2.plot(hours, zaibai_hourly.values, color=lightning_color, linestyle='--', linewidth=2)

    ax1.set_title(f"{date} {sav} / {station}", fontsize=11)
    ax1.set_xticks(range(0, 24, 2))
    ax1.set_ylim(bottom=0)
    ax2.set_ylim(bottom=0)
    ax1.set_ylabel('Krituliai (mm)', color=rain_color)
    ax2.set_ylabel('Žaibai (vnt)', color=lightning_color)

plt.xlabel('Valanda')
plt.suptitle('10 savivaldybių, kuriose per 1 val. fiksuotas didžiausias žaibų kiekis ir tos dienos kritulių kiekio pasiskirstymas', fontsize=14)
plt.tight_layout(rect=[0, 0, 1, 0.96])
plt.show()
No description has been provided for this image

Ryšys tarp žaibų ir kritulių:

In [45]:
df_krituliai['dg_obstime'] = pd.to_datetime(df_krituliai['dg_obstime'])
df_zaibai['obstime'] = pd.to_datetime(df_zaibai['obstime'])

df_krituliai['hour'] = df_krituliai['dg_obstime'].dt.floor('h')
df_zaibai['hour'] = df_zaibai['obstime'].dt.floor('h')

krituliai_per_val = df_krituliai.groupby('hour')['rr_per'].sum().reset_index()

zaibai_per_val = df_zaibai.groupby('hour')['kiekis'].sum().reset_index()

df_valanda = pd.merge(krituliai_per_val, zaibai_per_val, on='hour', how='outer')
df_valanda = df_valanda.fillna(0) 

sns.regplot(data=df_valanda, x='kiekis', y='rr_per')
plt.xlabel('Žaibų skaičius per valandą')
plt.ylabel('Kritulių kiekis per 1 val. (mm)')
plt.title('Krituliai bei žaibai per 1 val.')
plt.show()
No description has been provided for this image

*Išvados*

Išnagrinėjus 2023-2025 m. žaibų išlydžių kiekio pasiskirstymą, matoma, kad skirtingais metais žaibų kiekis ir jo pasiskirstymas kinta, tačiau bendra tendencija, kad žaibų išlydžiai dažniausiai fiksuojami vasaros laikotarpiu pasitvirtina.

Vertinant bendrą šalies tendenciją ne visais atvejais didelis kritulių kiekis indikuoja apie didelį žaibų išlydžių kiekį.

Analizuojant dienų ir konkrečių meteorologijos stočių, kuriose buvo išmatuotas didelis kritulių kiekis atvejus, matoma tendencija, kad esant dideliam kritulių kiekiui, stebimas ir didesnis žaibų išlydžių kiekis, tačiau ryšys nėra ryškus, nes fiksuojami ir atvejai su krituliais be žaibų ir su žaibais be kritulių.

Vertinant savivaldybes, kuriose tam tikromis valandomis užfiksuotas didžiausias žaibų išlydžių skaičius ir kritulių kiekio pasiskirstymas, matyti, kad beveik visais atvejais, esant dideliam žaibų išlydžių kiekiui, stebimas ir didesnis kritulių kiekis. Todėl galima daryti išvadą, kad didelis žaibų išlydžių kiekis turi stipresnį ryšį su kritulių kiekiu nei atvejai, kai fiksuojamas tik didelis kritulių kiekis.

Didelė duomenų sklaida, tais laikotarpiais, kai žaibų ir kritulių reikšmės mažos yra labai tipiška gamtos reiškiniams, kurie iš tiesų kintantys ir nevienodi. Todėl galima teikti, kad ryšys tarp kritulių kiekio ir žaibų išlydžių skaičiaus yra, tačiau ne visuomet glaudus.

*Duomenų šaltinis*

Darbe naudojami 55 meteorologijos stočių kritulių kiekio kiekvienos valandos duomenys, bei žaibų išlydžių Lietovos teritorijoje duomenys iš Lietuvos hidrometeorologijos tarnybos (www.meteo.lt)

In [ ]: