Dane PIT po kodach TERC¶
MF od 2022 r. oprócz corocznych statystyk z akcji PIT publikuje również od 2022 statystyki dotyczące liczby podatników, dochodów oraz podatku po formach rozliczenia wg gmin, po kodach TERC. Dane dosepne są tutaj.
Są dostepne w różnych formatach, najmniej przy tym waży Excel, który można po prostu sobie ściagnąć i zaczytać. Poniżej kilka pierwszych obserwacji. Uwaga jest taka, że jeśli na jednaj formie nie występowało w danej gminie 6 lub więcej osób, to trafiałą ona do puli zbiorczej, nieprzypisanych podatników.
%matplotlib inline
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import geopandas as gpd
from pysal.lib import weights
from pysal.explore import esda
from splot.esda import plot_moran
import os
# Project directory
pDir = "~/tcsrv/projects/data/pit_terc/"
mapDir = "~/tcsrv/data/mapy/prg_2023/"
pit_2023 = pd.read_excel(
os.path.join(pDir, "dane", "pit_2023.xlsx"),
dtype={'Kod województwa':str, 'Kod powiatu': str, 'Kod gminy': str}
)
# Dodaj 0 do kodów terc - przyda się przy tworzeniu kodów do merge
pit_2023['Kod województwa'] = pit_2023['Kod województwa'].str.pad(width=2,side='left',fillchar='0')
pit_2023['Kod powiatu'] = pit_2023['Kod powiatu'].str.pad(width=2,side='left',fillchar='0')
pit_2023['Kod gminy'] = pit_2023['Kod gminy'].str.pad(width=3,side='left',fillchar='0')
pit_2023.head()
W tabeli poniżej kilka podsumowań.
Łącznie dane zwierają informacje dla ok. 28,6 mln deklaracji. Może się to wydawać więcej niż jest podatnikó, ale to wynika z faktu, że jedna osoba może różne deklaracje składać, np. rozliczać dochody z pracy na skali podatkowej a dochody z wynajmu np. ryczałtem.
Przychody oczywiście nie obejmują dla działalności gospodarczej (DG) kosztów. Stąd lepiej posługiwać się kategorią dochodu, która też nie jest do końca idealna, ponieważ w przypadku umowy o pracę (UoP) zawiera także ryczałtowe koszty uzyskania przychodu, które to kosztami nie są, ale obniżają podatek.
Kwoty podane w kolumnach są w tys. zł, ale dla czytelności przeliczone na mld zł.
Czyli łącznie mamy 1 632 mld zł dochodów od których odprowadzono 143,9 mld zł podatku PIT (dla ryczałtu brak info o kosztach, więc dochód == 0).
pit_2023.iloc[:, 8:13].sum() / 1e6
Naturalne jest, że grupą zarabiającą najwięcej są liniowcy ze średnim dochodem ponad 300 tys. rocznie, co widać w poniższych podsumowaniach. To tutaj znajdziemy osoby o najwyższych dochodach z działalności. Dla porównania osoby uzyskujące dochody z działalności na skali podatkowej miały w 2023 r. średnie dochody na poziomie trochę ponad 66 tys. zł. Jest to zbliżone do dochodów ze skali podatkowej poza działalnością (ponad 55 tys. zł rocznie).
Co ciekawe grupą o najniższych średnichdochodach byli podatnicy rozliczający dochody z giełdy. No ale pamiętć należy, że patrzymy na średnią a nie na cały rozkład.
po_formach_2023 = pit_2023.iloc[:, 7:13].groupby("Nazwa formularza").sum() / 1e6
po_formach_2023
Ciekawe jest też, że średnie dochody osób na skali podatkowej (PIT-37 i PIT-36) urosły między 2022 a 2022 mocniej niż na podatku liniowym (PIT-36) i podatku od zysku z giełdy (PIT-38). Do tego na ryczałcie sredni przychoód też wzrósł o wiele więcej niż na podatku liniowym.
# Ładuj dane za 2022 r.
pit_2022 = pd.read_excel(
os.path.join(pDir, "dane", "pit_2022.xlsx")
)
po_formach_2022 = pit_2022.iloc[:, 7:13].groupby("Nazwa formularza").sum() / 1e6
# Licz średnie
sredni_dochod_23 = po_formach_2023.iloc[:, 2] / po_formach_2023.iloc[:, 0] * 1000
sredni_dochod_23.iloc[0] = \
po_formach_2023.iloc[po_formach_2023.index == 'PIT-28', 1] / po_formach_2023.iloc[po_formach_2023.index == 'PIT-28', 0] * 1000
sredni_dochod_22 = po_formach_2022.iloc[:, 2] / po_formach_2022.iloc[:, 0] * 1000
sredni_dochod_22.iloc[0] = \
po_formach_2022.iloc[po_formach_2022.index == 'PIT-28', 1] / po_formach_2022.iloc[po_formach_2022.index == 'PIT-28', 0] * 1000
# Do tabeli
po_formach_22_23 = pd.concat([sredni_dochod_22, sredni_dochod_23], axis=1)
po_formach_22_23.columns = ['Średnia 2022', 'Średnia 2023']
po_formach_22_23['Zmiana r/r %'] = po_formach_22_23['Średnia 2023'] / po_formach_22_23['Średnia 2022'] * 100 - 100
po_formach_22_23.style \
.format(precision=1, thousands=" ", decimal=",")
Mapy¶
Oczywiście grzechem by było nie skorzystać z możliwosci wizualizacji tych na danych na mapach. Tutaj nieodżałowaną pomocą jest Państwowy Rejestr Granic, gdzie bez problemu można ściągnać pliki SHP do mapek.
try:
pl_gminy = gpd.read_file(os.path.join(mapDir, 'A03_Granice_gmin.shp'))
woj = gpd.read_file(os.path.join(mapDir, 'A01_Granice_wojewodztw.shp'))
print(pl_gminy.columns)
except ValueError as e:
print(e)
pl_gminy.head()
fig, ax = plt.subplots(1, figsize=(14, 14))
pl_gminy.plot(ax=ax, linewidth=0.5, edgecolor='0.4', color='0.8')
ax.axis('off')
# Dodaj kod TERC
pit_2023['terc'] = pit_2023['Kod województwa'] + pit_2023['Kod powiatu'] + pit_2023['Kod gminy']
pit_2023_mapa = pl_gminy.merge(pit_2023, left_on='JPT_KOD_JE', right_on='terc', how='left')
pit_2023_mapa['sredni_doch'] = pit_2023_mapa['Kwota dochodu w tys. złotych'] / pit_2023_mapa['Liczba podatników']
pit_2023_mapa['sredni_przychod'] = pit_2023_mapa['Kwota przychodu w tys. złotych '] / pit_2023_mapa['Liczba podatników']
pit_2023_mapa.head()
fig, ax = plt.subplots(1, figsize=(12, 12))
woj.plot(ax=ax, edgecolor='0', color='0')
pit_2023_mapa[pit_2023_mapa['Nazwa formularza'] == 'PIT-37'].plot(
ax=ax,
column='sredni_doch',
cmap='Spectral_r',
legend=True,
scheme='quantiles',
k=10,
legend_kwds={'loc': 'lower left'} # This line positions the legend in the bottom left
)
ax.set_title('Średni dochód na podatnika: PIT-37', fontdict={'fontsize': 20}, loc='left')
ax.axis('off')
fig, ax = plt.subplots(1, figsize=(12, 12))
woj.plot(ax=ax, edgecolor='0', color='0')
pit_2023_mapa[pit_2023_mapa['Nazwa formularza'] == 'PIT-36'].plot(
ax=ax,
column='sredni_doch',
cmap='Spectral_r',
legend=True,
scheme='quantiles',
k=10,
legend_kwds={'loc': 'lower left'} # This line positions the legend in the bottom left
)
ax.set_title('Średni dochód na podatnika: PIT-36', fontdict={'fontsize': 20}, loc='left')
ax.axis('off')
fig, ax = plt.subplots(1, figsize=(12, 12))
woj.plot(ax=ax, edgecolor='0', color='0')
pit_2023_mapa[pit_2023_mapa['Nazwa formularza'] == 'PIT36L'].plot(
ax=ax,
column='sredni_doch',
cmap='Spectral_r',
legend=True,
scheme='quantiles',
k=10,
legend_kwds={'loc': 'lower left'} # This line positions the legend in the bottom left
)
ax.set_title('Średni dochód na podatnika: PIT-36L', fontdict={'fontsize': 20}, loc='left')
ax.axis('off')
fig, ax = plt.subplots(1, figsize=(12, 12))
woj.plot(ax=ax, edgecolor='0', color='0')
pit_2023_mapa[pit_2023_mapa['Nazwa formularza'] == 'PIT-28'].plot(
ax=ax,
column='sredni_przychod',
cmap='Spectral_r',
legend=True,
scheme='quantiles',
k=10,
legend_kwds={'loc': 'lower left'} # This line positions the legend in the bottom left
)
ax.set_title('Średni dochód na podatnika: PIT-28', fontdict={'fontsize': 20}, loc='left')
ax.axis('off')
Analiza¶
No dobrze, obrazki pracudnej urody, ale wydaje się, że dochody zarówno z DG na skali jak i pozostałe (PIT-36 i PIT-37) są skoncentrowane wokół miast, natomiast średnie dochody z podatku liniowego i przychody z ryczałtu wydają się bardziej rozproszone.
Do weryfikacji tego może warto użyć bardziej zaawansowanej analityki niż tylko kolorowe mapki.
Bardzo ciekawy materiał odnośnie analiz przestrzennych w Pythonie dostępny jest tutaj.
Zacznę od stworzenia wag do obliczenia współczynnika autokorelacji Maran's I.
# Formularze
forms = ['PIT-37', 'PIT-36', 'PIT36L', 'PIT-28']
# Stwórz wagi i policz Moran I
moranI = {}
for form in forms:
df = pit_2023_mapa[pit_2023_mapa['Nazwa formularza'] == form]
# Wagi
w = weights.KNN.from_dataframe(df, k=8)
w.transform = "R" # Row-standardize the weights
# Moran's I
if form == 'PIT-28':
moranI[form] = esda.moran.Moran(df['sredni_przychod'], w)
else:
moranI[form] = esda.moran.Moran(df['sredni_doch'], w)
No i ostatecznie sam współczynnik. Potwierdza się intuicja. Dla linii i ryczałtu autokorelacja jest bliżej 0, co oznacza, że średnie wielkości dochodów tych podatników są od siebie mniej przestrzennie zależne niż dla dochodów ze skali.
# Print results
for key, value in moranI.items():
print(f"{key}: {value.I:.3f}, pSim: {value.p_sim:.3f}")
plot_moran(moranI['PIT-36'])
plot_moran(moranI['PIT-36L'])
plot_moran(moranI['PIT-28'])