Vyresnio amžiaus asmenų užimtumo veiksniai ¶

Šio darbo tikslas: Išanalizuoti vyresnio amžiaus asmenų užimtumo veiksnius

Iškeltos hipotezės:

H1: Vyresnio amžiaus asmenų dalyvavimas darbo rinkoje yra mažesnis nei jaunesnių;

H2: Užimtumo struktūroje vyrai dalyvauja ilgiau už moteris;

H3: Vyresnio amžiaus asmenys yra linkę prasčiau vertinti savo sveikatą;

H4: Aukštesnis išsilavinimas yra susijęs su aktyvesniu užimtumu vyresniame amžiuje.

Naudojami duomenys: Europos socialinio tyrimo* (angl. European Social Survey, ESS) naujausi 10 bangos (2020-2022 m.) duomenys. Užduočiai atlikti pasirinktas sociodemografinių kintamųjų modulis.

*Tyrimo apklausos vykdomos kas du metus, iki šiol tai yra kokybiškiausias socialinis tyrimas Europoje. Duomenys viešai prieinami susikūrus profilį oficialioje svetainėje (https://ess.sikt.no/en/).

1. Duomenų įkėlimas¶

1.1. Duomenų įkėlimas prisijungus prie MySQL serverio¶

Svarbu pažymėti, kad duomenų įkėlimas prisijungus prie MySQL serverio naudojamas tik pavaizduoti, jog prisijungti prie sukurtos duomenų bazės pavyko. Tačiau analizei bus naudojamas pilnas csv failas, kurio įkelti į duomenų bazę nepavyko (kuriant duomenų bazę kilo iššūkių dėl itin ilgo užpildymo laiko).

In [ ]:
#!pip install ipython-sql   #Requirement already satisfied
In [ ]:
# pip install mysqlclient   #Requirement already satisfied
In [14]:
import sqlalchemy
In [15]:
sqlalchemy.create_engine('mysql://root:Tikslas@localhost/ess10_db')
Out[15]:
Engine(mysql://root:***@localhost/ess10_db)
In [16]:
%load_ext sql
The sql extension is already loaded. To reload it, use:
  %reload_ext sql
In [17]:
%sql mysql://root:Tikslas@localhost/ess10_db
In [18]:
%%sql
show tables
 * mysql://root:***@localhost/ess10_db
1 rows affected.
Out[18]:
Tables_in_ess10_db
ess10_sql
In [20]:
%%sql
select * from ess10_sql limit 10
 * mysql://root:***@localhost/ess10_db
10 rows affected.
Out[20]:
idno country gender age health
10002 BG 2 76 2
10003 FI 2 24 1
10004 CH 1 57 2
10005 FR 2 23 2
10006 BG 1 43 2
10007 FR 2 32 2
10009 BG 2 50 3
10011 FR 2 25 2
10014 CH 2 33 1
10017 FI 2 33 4

1.2. Duomenų įkėlimas iš csv¶

In [23]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
In [34]:
tyrimas = pd.read_csv("ess.csv", index_col=0)
tyrimas.head()
Out[34]:
country gender age health marital children domicil edu_isced eduyrs activity ... hinctnta hincfel father_edu emprf14 occf14b mother_edu emprm14 occm14b course region
idno
10038 BE 2 16.0 2 6.0 2.0 4.0 1.0 11.0 2.0 ... NaN 2.0 4.0 1.0 NaN 6.0 1.0 5.0 2.0 BE24
10053 BE 2 24.0 2 6.0 2.0 4.0 7.0 18.0 3.0 ... NaN 1.0 7.0 1.0 2.0 7.0 2.0 3.0 1.0 BE24
10055 BE 1 58.0 2 1.0 2.0 4.0 6.0 18.0 5.0 ... 7.0 2.0 5.0 4.0 NaN 5.0 1.0 3.0 2.0 BE33
10062 BE 1 35.0 1 4.0 1.0 1.0 4.0 12.0 1.0 ... 7.0 2.0 1.0 2.0 8.0 1.0 3.0 NaN 2.0 BE21
10064 BE 1 61.0 1 6.0 2.0 2.0 7.0 19.0 1.0 ... 6.0 1.0 6.0 1.0 3.0 6.0 1.0 3.0 2.0 BE24

5 rows × 24 columns

In [35]:
tyrimas.shape
Out[35]:
(37611, 24)
In [36]:
tyrimas.info()
<class 'pandas.core.frame.DataFrame'>
Index: 37611 entries, 10038 to 27858
Data columns (total 24 columns):
 #   Column              Non-Null Count  Dtype  
---  ------              --------------  -----  
 0   country             37611 non-null  object 
 1   gender              37611 non-null  int64  
 2   age                 37319 non-null  float64
 3   health              37611 non-null  int64  
 4   marital             37547 non-null  float64
 5   children            24300 non-null  float64
 6   domicil             37581 non-null  float64
 7   edu_isced           37487 non-null  float64
 8   eduyrs              37276 non-null  float64
 9   activity            37578 non-null  float64
 10  emplrel             33399 non-null  float64
 11  contract            28963 non-null  float64
 12  organisation        33363 non-null  float64
 13  main_source_income  37092 non-null  float64
 14  hinctnta            29380 non-null  float64
 15  hincfel             37452 non-null  float64
 16  father_edu          34659 non-null  float64
 17  emprf14             36431 non-null  float64
 18  occf14b             32863 non-null  float64
 19  mother_edu          32905 non-null  float64
 20  emprm14             36944 non-null  float64
 21  occm14b             22604 non-null  float64
 22  course              37518 non-null  float64
 23  region              37611 non-null  object 
dtypes: float64(20), int64(2), object(2)
memory usage: 7.2+ MB

2. Duomenų tvarkymas¶

  1. Perkoduojami kintamieji į labiau suprantamas reikšmes.
  2. Praleistos reikšmės iš anksto buvo sutvarkytos naudojant Power BI.
  3. Išfiltruojami tik Lietuvos duomenys, kad atspindėtų šios šalies populiaciją.
  4. Tyrime dalyvavo 1659 respondentai iš Lietuvos.
In [106]:
tyrimas.rename(columns={'domicil': 'area', 
                        'hinctnta': 'income',
                        'hincfel': 'feel_inc',
                        'emprf14': 'father_empl',
                        'occf14b': 'father_occ',
                        'emprm14': 'mother_empl',
                        'occm14b': 'mother_occ'}, inplace=True)
tyrimas.head()
Out[106]:
country gender age health marital children area_live edu_isced eduyrs activity ... income_decil feel_inc father_edu father_empl father_occ mother_edu mother_empl mother_occ course region
idno
10038 BE 2 16.0 2 6.0 2.0 4.0 1.0 11.0 2.0 ... NaN 2.0 4.0 1.0 NaN 6.0 1.0 5.0 2.0 BE24
10053 BE 2 24.0 2 6.0 2.0 4.0 7.0 18.0 3.0 ... NaN 1.0 7.0 1.0 2.0 7.0 2.0 3.0 1.0 BE24
10055 BE 1 58.0 2 1.0 2.0 4.0 6.0 18.0 5.0 ... 7.0 2.0 5.0 4.0 NaN 5.0 1.0 3.0 2.0 BE33
10062 BE 1 35.0 1 4.0 1.0 1.0 4.0 12.0 1.0 ... 7.0 2.0 1.0 2.0 8.0 1.0 3.0 NaN 2.0 BE21
10064 BE 1 61.0 1 6.0 2.0 2.0 7.0 19.0 1.0 ... 6.0 1.0 6.0 1.0 3.0 6.0 1.0 3.0 2.0 BE24

5 rows × 24 columns

In [47]:
# Išfiltruojami tik Lietuvos duomenys
df1 = tyrimas[(tyrimas['country'] == 'LT')]
In [58]:
# Patikrinama, kiek respondentų yra imtyje
df1.shape
Out[58]:
(1659, 24)

3. Tyrimo imties charakteristikos¶

Susipažįstama su tyrimo imtimi:

  1. Didžiąją dalį respondentų sudaro moterys, vyrų kiek mažiau;
  2. Gausiausios amžiaus kategorijos yra 51-65 ir 66+;
  3. Didžioji dalis yra dirbantys arba pensininkai.
In [69]:
# Pagal lytį 1-vyras, 2-moteris
sns.countplot(x='gender', data=df1, palette='rocket')
plt.show()
In [119]:
df1["gender"].value_counts()
Out[119]:
gender
2    1021
1     638
Name: count, dtype: int64
In [79]:
# Pagal amžių gaunasi netvarkingai, todėl sukuriamos amžiaus grupės
def age_group(age):
    if age <= 20:
        return '0-20'
    elif age <= 35:
        return '21-35'
    elif age <= 50:
        return '36-50'
    elif age <= 65:
        return '51-65'
    else:
        return '66+'

# Sukuriamas naujas stulpelis
df1['age group'] = df1['age'].apply(age_group)
C:\Users\viole\AppData\Local\Temp\ipykernel_8608\2823441412.py:15: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df1['age group'] = df1['age'].apply(age_group)
In [77]:
sns.countplot(x='age group', data=df1, palette='rocket')
plt.show()
In [120]:
df1["age group"].value_counts()
Out[120]:
age group
51-65    484
66+      409
36-50    387
21-35    304
0-20      75
Name: count, dtype: int64
In [80]:
# Pagal užimtumą: 1- Paid work, 2- Education, 3- Unemployed, looking for job, 4 - Unemployed, not looking for job, 5 - Permanently sick or disabled
# 6- Retired, 7- Community or military service, 8 - Housework, looking after children, others, 9- Other
# Nors duomenys buvo tvarkyti, tačiau vis vien įsimaišė 77 - refusal
sns.countplot(x='activity', data=df1, palette='rocket')
plt.show()
In [121]:
# Pagal užimtumą: 1- Paid work, 2- Education, 3- Unemployed, looking for job, 4 - Unemployed, not looking for job, 5 - Permanently sick or disabled
# 6- Retired, 7- Community or military service, 8 - Housework, looking after children, others, 9- Other
# Nors duomenys buvo tvarkyti, tačiau vis vien įsimaišė 77 - refusal
df1["activity"].value_counts()
Out[121]:
activity
1.0     806
6.0     438
2.0      98
8.0      84
3.0      79
5.0      75
4.0      40
77.0     27
9.0      12
Name: count, dtype: int64

4. Tikrinama H1: Vyresnio amžiaus asmenų dalyvavimas darbo rinkoje yra mažesnis nei jaunesnių¶

Grafikas rodo, kad apmokamas darbas yra linkęs mažėti 51-65 metų amžiaus grupėje. Tačiau ši informacija nėra labai tiksli, nes asmenų pasiskirstymas amžiaus grupėse yra skirtingas. Dėl šios priežasties, skaičiuojama procentinė išraiška:

  1. 36-50 metų amžiaus grupėje 79 proc. yra dirbantys apmokamą darbą, 6 proc. - namų šeimininkai (-ės), 5 - proc. bedarbiai.
  2. 51-65 metų amžiaus grupėje 58 proc. yra dirbantys apmokamą darbą, 12 proc. - pensininkai, 11 - proc. serga arba turi negalią, 10 proc. - bedarbiai.

Galima teigti, kad vyresnio amžiaus asmenų dalyvavimas darbo rinkoje yra mažesnis nei jaunų. Svarbu pažymėti, kad iš darbo rinkos pasitraukiama ne tik sulaukus senatvės pensijos amžiaus, bet ir dėl ligos, negalios, nedarbo.

In [81]:
# Tikrinama kaip apmokamas darbas pasiskirsto pagal amžiaus grupes
df2 = df1[(df1['activity'] == 1)]
In [82]:
sns.countplot(x='age group', data=df2, palette='rocket')
plt.show()
In [85]:
# Ankstesniu skaičiavimu negalima pilnai pasitikėti, nes vaizduojami absoliutūs skaičiai.
# Tarp dviejų amžiaus grupių nedidelis skirtumas, tad įsitikinimui reikėtų procentinės išraiškos.
df3 = df1[(df1['age group'] == "36-50")]
In [87]:
# Skaičiuojami procentai "36-50" grupei
jaunesni = df3['activity'].value_counts()
total_count = len(df3)
jaunesni_proc = (jaunesni / total_count) * 100
jaunesni_proc
Out[87]:
activity
1.0     79.069767
8.0      6.201550
3.0      4.651163
5.0      3.100775
77.0     2.842377
4.0      2.583979
6.0      0.775194
9.0      0.775194
Name: count, dtype: float64
In [88]:
# Skaičiuojami procentai "51-65" grupei
df4 = df1[(df1['age group'] == "51-65")]
In [89]:
vyresni = df4['activity'].value_counts()
total_count = len(df4)
vyresni_proc = (vyresni / total_count) * 100
vyresni_proc
Out[89]:
activity
1.0     58.264463
6.0     11.983471
5.0     10.537190
3.0      9.504132
4.0      4.132231
8.0      3.305785
9.0      1.446281
77.0     0.619835
2.0      0.206612
Name: count, dtype: float64

5. Tikrinama H2: Užimtumo struktūroje vyrai dalyvauja ilgiau už moteris¶

Grafikas rodo, kad šioje imtyje moteris darbo rinkoje dalyvauja net iki 83 metų, o vyras iki 78 metų. Tačiau šių rezultatų nepakanka daryti išvadų apie visą amžiaus grupę. 65+ amžiaus grupėje iš dirbančių asmenų 53 proc. yra vyrai, o 47 proc. moterys. Galima teigti, kad nepaisant to, kad imtyje ilgiausiai užimtume dalyvaujanti yra moteris, visoje amžiaus kategorijoje vyrai aktyvumą išlaiko ilgiau.

In [91]:
df5 = df1[(df1['activity'] < 77)] # pašalinamas netyčia pasipainiojęs "refusal"
In [94]:
# Pagal lytį 1-vyras, 2-moteris
sns.jointplot(x='age', 
              y='activity', 
              data=df5,
               hue='gender')
             
plt.show()
C:\Users\viole\anaconda3\Lib\site-packages\seaborn\_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.
  with pd.option_context('mode.use_inf_as_na', True):
C:\Users\viole\anaconda3\Lib\site-packages\seaborn\_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.
  with pd.option_context('mode.use_inf_as_na', True):
In [96]:
df2.groupby(["gender"])["age"].max()
Out[96]:
gender
1    78.0
2    83.0
Name: age, dtype: float64
In [130]:
# Ankstesnių rezultatų nepakanka daryti išvadų apie visą amžiaus grupę, todėl skaičiuojama toliau
ilgiausiai_dirba = df1[(df1['age group'] == "66+") & (df1['activity'] == 1)]
In [131]:
# Skaičiuojami procentai pagal lytį
lytis = ilgiausiai_dirba['gender'].value_counts()
total_count = len(ilgiausiai_dirba)
lytis_proc = (lytis/ total_count) * 100
lytis_proc
Out[131]:
gender
1    52.941176
2    47.058824
Name: count, dtype: float64

6. Tikrinama H3: Vyresnio amžiaus asmenys yra linkę prasčiau vertinti savo sveikatą¶

Grafikas rodo, kad asmenys savo sveikatą subjektyviai kaip "blogą" pradeda vertinti gana anksti, nuo 25 metų, tačiau didėjant amžiui toks vertinimas dažnėja, o kaip "labai blogą" pradeda vertinti nuo 50 metų. Analizuojant detaliau, matoma, kad kiekvienoje amžiaus kategorijoje sveikatos vertinimas didėja t.y. prastėja (didesnė reikšmė - prastesnis vertinimas). Ankstesnėje tyrimo dalyje buvo matyti, kad sveikata sąlygoja užimtumo galimybes 51-65 metų amžiaus grupėje 11 - proc. nedirbo dėl ligos arba negalios.

In [101]:
df6 = df1[(df1['health'] < 6)] # pašalinamas pasipainiojęs "refusal"
In [102]:
# 1	Very good; 2 Good; 3 Fair; 4 Bad; 5	Very bad

sns.regplot(data=df6, 
            x='age', 
            y='health') 
plt.show()
In [104]:
# Patikrinama kaip vertinimų vidurkiai pasiskirsto amžiaus grupėse
df6.groupby(["age group"])["health"].mean()
Out[104]:
age group
0-20     1.800000
21-35    1.953947
36-50    2.222798
51-65    2.691511
66+      3.132353
Name: health, dtype: float64

7. Tikrinama H4: Aukštesnis išsilavinimas yra susijęs su aktyvesniu užimtumu vyresniame amžiuje.¶

Iš grafiko galima matyti, kad asmenys, kurie ilgiau mokėsi (trukmė metais), dalyvauja darbo rinkoje ir sulaukę > 55 metų amžiaus. Tie, kurie mokėsi iki 10 metų, tie darbo rinkoje nebedalyvauja.

In [134]:
# Reikalingi tik dirbantys
df1['dirbantys'] = df1['activity'] == 1
C:\Users\viole\AppData\Local\Temp\ipykernel_8608\3863111863.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df1['dirbantys'] = df1['activity'] == 1
In [133]:
# Reikia skaitinio stulpelio
df1['dirba_binarinis'] = df1['dirbantys'].astype(int)
df1.head()
C:\Users\viole\AppData\Local\Temp\ipykernel_8608\1416501714.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df1['dirba_binarinis'] = df1['dirbantys'].astype(int)
Out[133]:
country gender age health marital children area_live edu_isced eduyrs activity ... mother_edu mother_empl mother_occ course region age group ar dirba ardirba dirbantys dirba_binarinis
idno
10018 LT 1 24.0 1 1.0 2.0 1.0 4.0 12.0 1.0 ... NaN 4.0 NaN 2.0 LT022 21-35 1 1 True 1
10024 LT 2 74.0 3 1.0 1.0 3.0 4.0 11.0 6.0 ... NaN 1.0 7.0 2.0 LT026 66+ 2 2 False 0
10039 LT 2 37.0 2 6.0 NaN 3.0 5.0 13.0 1.0 ... 5.0 1.0 5.0 2.0 LT027 36-50 1 1 True 1
10054 LT 2 49.0 1 4.0 2.0 4.0 6.0 14.0 1.0 ... 6.0 1.0 7.0 2.0 LT022 36-50 1 1 True 1
10059 LT 1 54.0 4 1.0 2.0 3.0 3.0 11.0 1.0 ... 4.0 1.0 1.0 2.0 LT029 51-65 1 1 True 1

5 rows × 29 columns

In [137]:
# Remiamasi mokslininkų tyrimais, kad užimtumas mažėja nuo 55 metų. Tuo pačiu pašalinami pasipainioję "refusal"
df7 = df1[(df1['age'] > 55) & (df1['eduyrs'] < 50)]
In [138]:
df7.plot.scatter('eduyrs', 'dirba_binarinis')
plt.show()

8. Mašininis mokymasis¶

Tęsiant analizę matyti, kad logistinės regresijos tikslumas 70 proc., tokiu tikslumu nusakoma, ar asmuo, sulaukęs 55 metų dirbs, atsižvelgiant į jo mokymosi trukmę.

In [139]:
# Mašininio mokymosi dalis
from sklearn.linear_model import LogisticRegression
In [152]:
X=df7[["eduyrs"]] 
X.head()
Out[152]:
eduyrs
idno
10024 11.0
10061 13.0
10076 16.0
10079 16.0
10090 13.0
In [144]:
y=df7["dirba_binarinis"] 
y.head()
Out[144]:
idno
10024    0
10061    0
10076    0
10079    0
10090    0
Name: dirba_binarinis, dtype: int32
In [145]:
model = LogisticRegression()
model
Out[145]:
LogisticRegression()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
LogisticRegression()
In [147]:
from sklearn.model_selection import train_test_split
In [153]:
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
X_train
Out[153]:
eduyrs
idno
23918 13.0
11989 15.0
12712 11.0
15430 8.0
24832 16.0
... ...
24227 14.0
10283 15.0
25616 11.0
24175 14.0
27152 12.0

568 rows × 1 columns

In [154]:
model.fit(X_train, y_train)
Out[154]:
LogisticRegression()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
LogisticRegression()
In [155]:
model.predict(X_test)
Out[155]:
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
       0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
In [183]:
# Modelio tikslumas 70 proc.
model.score(X_test, y_test)
Out[183]:
0.7089201877934272
In [167]:
from sklearn import metrics
In [168]:
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
model = LogisticRegression()
model.fit(X_train, y_train)
y_predicted = model.predict(X_test)
print(model.score(X_test, y_test))
print(metrics.confusion_matrix(y_test, y_predicted))
0.7089201877934272
[[149   3]
 [ 59   2]]
In [185]:
cf_matrix = metrics.confusion_matrix(y_test, y_predicted)
sns.heatmap(cf_matrix/np.sum(cf_matrix), annot=True, 
fmt='.2%', cmap='Blues')
Out[185]:
<Axes: >
In [170]:
pd.DataFrame(metrics.confusion_matrix(y_test, y_predicted),
             columns=['nedirba', 'dirba'],
             index=['nedirba', 'dirba']).rename_axis(index='Actual', columns='predicted')
Out[170]:
predicted nedirba dirba
Actual
nedirba 149 3
dirba 59 2

Išvados¶

Rezultatai rodo, kad vyresnio amžiaus asmenų užimtumas yra sąlygojamas jų amžiaus, lyties, sveikatos būklės bei turimo išsilavinimo. Iškeltos hipotezės buvo patvirtintos.

H1: Vyresnio amžiaus asmenų dalyvavimas darbo rinkoje yra mažesnis nei jaunesnių; PATVIRTINTA

H2: Užimtumo struktūroje vyrai dalyvauja ilgiau už moteris; PATVIRTINTA

H3: Vyresnio amžiaus asmenys yra linkę prasčiau vertinti savo sveikatą; PATVIRTINTA

H4: Aukštesnis išsilavinimas yra susijęs su aktyvesniu užimtumu vyresniame amžiuje. PATVIRTINTA