Dati del Report di Roma - Abitare¶

In [1]:
import pandasdmx as sdmx
import pandas as pd
import plotly.graph_objects as go

import plotly.io as pio
pio.renderers.default = "plotly_mimetype+notebook"

Fonte OECD - OCSE¶

In [2]:
source_oecd = {
    "id": "OECD_ADV",
    "api_id": "OECD.ELS.SAE",
    "name": "OECD",
    "url": "https://sdmx.oecd.org/public/rest",
    "supports": {
        "provisionagreement": False,
        "structure-specific data": False
    }
}
sdmx.add_source(source_oecd, override=True)
oecd = sdmx.Request('OECD_ADV')
In [3]:
# flow_msg = oecd.dataflow()
# dataflows = pd.DataFrame(sdmx.to_pandas(flow_msg.dataflow)).reset_index(names='COD').rename(columns={0:'DESCR_IT'})
In [4]:
data_msg = oecd.data('DSD_EARNINGS@AV_AN_WAGE')
data_df = pd.DataFrame(sdmx.to_pandas(data_msg)).reset_index()
data_df = data_df[(data_df['REF_AREA'].isin(['ITA','OECD']))&(data_df['PRICE_BASE']=='Q')&(data_df['UNIT_MEASURE']=='USD_PPP')].copy()
data_df = data_df[['REF_AREA','TIME_PERIOD','value']].copy()
In [5]:
base_df = data_df[data_df['TIME_PERIOD']=='1996'].copy()
m = data_df.merge(base_df, on='REF_AREA', how='left')
m['v'] = m['value_x'] / m['value_y'] * 100
m['TIME_PERIOD'] = m['TIME_PERIOD_x'].astype(int)
In [6]:
layout = go.Layout(title='Salario medio - base 1996')
fig = go.Figure(layout=layout)

for ref in list(m['REF_AREA'].unique()):
    temp = m[m['REF_AREA']==ref].sort_values(by='TIME_PERIOD').copy()
    fig.add_trace(go.Scatter(x=temp['TIME_PERIOD'], y=temp['v'], name=ref,mode='lines+markers',marker=dict(size=5)))
fig.show()
In [7]:
data_msg = oecd.data('DSD_AN_HOUSE_PRICES@DF_HOUSE_PRICES')
data_df = pd.DataFrame(sdmx.to_pandas(data_msg)).reset_index()
In [8]:
df = data_df[(data_df['REF_AREA'].isin(['ITA','EA']))&(data_df['FREQ']=='A')&(data_df['MEASURE']=='RPI')&(data_df['TIME_PERIOD']>'1995')].copy()
df = df[['REF_AREA','TIME_PERIOD','value']].copy()
base_df = df[df['TIME_PERIOD']=='1996'].copy()
m = df.merge(base_df, on='REF_AREA', how='left')
m['v'] = m['value_x'] / m['value_y'] * 100
m['TIME_PERIOD'] = m['TIME_PERIOD_x'].astype(int)
layout = go.Layout(title='Canoni di affitto - base 1996')
fig = go.Figure(layout=layout)

for ref in list(m['REF_AREA'].unique()):
    temp = m[m['REF_AREA']==ref].sort_values(by='TIME_PERIOD').copy()
    fig.add_trace(go.Scatter(x=temp['TIME_PERIOD'], y=temp['v'], name=ref,mode='lines+markers',marker=dict(size=5)))
fig.show()
In [9]:
df = data_df[(data_df['REF_AREA'].isin(['ITA','EA']))&(data_df['FREQ']=='A')&(data_df['MEASURE']=='RHP')&(data_df['TIME_PERIOD']>'1995')].copy()
df = df[['REF_AREA','TIME_PERIOD','value']].copy()
base_df = df[df['TIME_PERIOD']=='1996'].copy()
m = df.merge(base_df, on='REF_AREA', how='left')
m['v'] = m['value_x'] / m['value_y'] * 100
m['TIME_PERIOD'] = m['TIME_PERIOD_x'].astype(int)
layout = go.Layout(title='Prezzo di compravendita - base 1996')
fig = go.Figure(layout=layout)

for ref in list(m['REF_AREA'].unique()):
    temp = m[m['REF_AREA']==ref].sort_values(by='TIME_PERIOD').copy()
    fig.add_trace(go.Scatter(x=temp['TIME_PERIOD'], y=temp['v'], name=ref,mode='lines+markers',marker=dict(size=5)))
fig.show()
In [ ]:
 

Fonte ISTAT¶

In [10]:
source_istat = {
    "id": "ISTAT_NEW",
    "api_id": "IT1",
    "name": "Instituto Nationale di Statistica (IT)",
    "documentation": "https://ec.europa.eu/eurostat/web/sdmx-web-services/rest-sdmx-2.1",
    "url": "https://esploradati.istat.it/SDMXWS/rest",
    "supports": {
      "provisionagreement": False,
      "structure-specific data": True
    }
  }
sdmx.add_source(source_istat, override=True)
istat = sdmx.Request('ISTAT_NEW', timeout=300)
istat.default_locale = "it"
flow_msg = istat.dataflow()
dataflows = pd.DataFrame(sdmx.to_pandas(flow_msg.dataflow)).reset_index(names='COD').rename(columns={0:'DESCR_IT'})
2024-09-18 18:08:44,085 pandasdmx.reader.sdmxml - DEBUG: Truncate sub-microsecond time in <Prepared>
In [11]:
dataflows[dataflows['DESCR_IT'].str.contains('abitaz', case=False)]
Out[11]:
COD DESCR_IT
267 143_497 Prezzi delle abitazioni (Ipab)
1503 27_474_DF_DCIS_SEPARAZIND1_6 Assegnazione dell'abitazione
1576 27_946_DF_DCIS_DIVORZIND1_6 Assegnazione dell'abitazione
1818 33_179 Affollamento nell'abitazione
1823 33_179_DF_DCCV_ABITAFFOLL_5 Livello del reddito e abitazione in affitto o ...
1825 33_225 Spesa per l'abitazione
1826 33_225_DF_DCCV_ABITSPESA_1 Abitazione in affitto o di proprietà
1832 33_290 Titolo di godimento dell'abitazione (in affitt...
1844 33_291_DF_DCCV_PROBLZONRES_2_5 Livello del reddito e abitazione in affitto o ...
1846 33_350 Spesa per l'abitazione delle famiglie con comp...
1847 33_350_DF_DCCV_ABITSPESA_STRA_1 Spesa per l'abitazione delle famiglie con comp...
1848 33_4 Abitazione danneggiata, umida o poco luminosa
1853 33_4_DF_DCCV_ABITPROBL_5 Livello del reddito e abitazione in affitto o ...
1899 34_217 Famiglie che considerano un carico pesante alc...
3234 82_87_DF_DCCV_AVQ_FAMIGLIE_16 Spazi esterni e riscaldamento nell'abitazione
3236 82_87_DF_DCCV_AVQ_FAMIGLIE_18 Cambio dell'abitazione
3237 82_87_DF_DCCV_AVQ_FAMIGLIE_19 Spese, acqua e altri problemi dell'abitazione
In [12]:
dataflows[dataflows['COD'].str.contains('143_497', case=False)]
Out[12]:
COD DESCR_IT
267 143_497 Prezzi delle abitazioni (Ipab)
268 143_497_DF_DCSP_IPAB_1 Trimestrali dal 2010 (base 2015)
269 143_497_DF_DCSP_IPAB_2 Medie annue dal 2010 (base 2015)
270 143_497_DF_DCSP_IPAB_3 Pesi dal 2010
271 143_497_DF_DCSP_IPAB_4 Trimestrali dal 2010 al 2016 (base 2010)
272 143_497_DF_DCSP_IPAB_5 Medie annue dal 2010 al 2016 (base 2010)

Prezzi delle abitazioni (Ipab)¶

In [13]:
cod = '143_497'
#cod = '143_497_DF_DCSP_IPAB_2'
In [14]:
exr_msg = istat.dataflow(cod)
exr_flow = exr_msg.dataflow[cod]
dsd = exr_flow.structure

dims = dict()
for dimension in dsd.dimensions:
    if dimension != 'TIME_PERIOD':
        dims[dimension] = sdmx.to_pandas(dsd.dimensions.get(dimension).local_representation.enumerated).to_dict()['name']
In [15]:
#dims.keys()
In [16]:
#dims['REF_AREA']['ITFG']
In [17]:
#dims['REF_AREA']['ITE43']
In [18]:
#dims['FREQ']
In [19]:
#dims['MEASURE']
In [20]:
#dims['PURCHASES_DWELLINGS']
In [21]:
#dims['DATA_TYPE']
In [22]:
key = {
    'DATA_TYPE': '60',
    'PURCHASES_DWELLINGS': 'ALL',
    'MEASURE': '4',
    'FREQ': 'A'
}
params = {
    'startPeriod':'2010'
}
try:
    data_msg = istat.data(cod,key=key,params=params)
    data_df = pd.DataFrame(sdmx.to_pandas(data_msg)).reset_index()
    #ref_area_list = list(data_df['REF_AREA'].unique())
    #procoms = [element for element in ref_area_list if self.check_procom(element)]
    #data_df = data_df[data_df['REF_AREA'].isin(procoms)]
    # data_df['AGE_G'] = data_df['AGE'].map(age_dict)
    # data_df['SEX'] = data_df['SEX'].map(sex_dict)
except Exception as err:
    print(err)

for field in dims.keys():
    data_df[field] = data_df[field].map(dims[field])
2024-09-18 18:08:58,616 pandasdmx.reader.sdmxml - INFO: Use supplied dsd=… argument for non–structure-specific message
In [23]:
data_df = data_df[['REF_AREA','TIME_PERIOD','value']].copy()
data_df= data_df[data_df['REF_AREA'].isin(['Italy', 'Torino', 'Milano', 'Roma', 'Mezzogiorno'])].copy()
In [24]:
layout = go.Layout(title='Indice di compravendita delle abitazioni, 2010-2023, base 2010')
fig = go.Figure(layout=layout)

for ref in list(data_df['REF_AREA'].unique()):
    temp = data_df[data_df['REF_AREA']==ref].sort_values(by='TIME_PERIOD').copy()
    times = list(temp['TIME_PERIOD'].unique())
    base_value = temp[temp['TIME_PERIOD']==times[0]]['value'].values[0]
    diff = base_value-100
    temp['value'] = temp['value'] - diff
    fig.add_trace(go.Scatter(x=temp['TIME_PERIOD'], y=temp['value'], name=ref,mode='lines+markers',marker=dict(size=5)))
fig.show()
In [25]:
dataflows[dataflows['COD'].str.contains('32_292', case=False)]
Out[25]:
COD DESCR_IT
1801 32_292 Reddito netto
1802 32_292_DF_DCCV_REDNETFAMFONTERED_1 Ampiezza della famiglia
1803 32_292_DF_DCCV_REDNETFAMFONTERED_2 Tipologia familiare
1804 32_292_DF_DCCV_REDNETFAMFONTERED_3 Minori in famiglia
1805 32_292_DF_DCCV_REDNETFAMFONTERED_4 Anziani in famiglia
1806 32_292_DF_DCCV_REDNETFAMFONTERED_5 Sesso del principale percettore
1807 32_292_DF_DCCV_REDNETFAMFONTERED_6 Classe di età del principale percettore
1808 32_292_DF_DCCV_REDNETFAMFONTERED_7 Titolo di studio del principale percettore
1809 32_292_DF_DCCV_REDNETFAMFONTERED_8 Condizione professionale del principale percet...
1810 32_292_DF_DCCV_REDNETFAMFONTERED_9 Regioni e tipo di comune

Reddito medio (Irpef)¶

In [26]:
dataflows[dataflows['DESCR_IT'].str.contains('irpef', case=False)]
Out[26]:
COD DESCR_IT
1725 30_1008 Reddito delle persone fisiche (Irpef) - comuni
In [27]:
#cod = '30_1008'
cod = '32_292_DF_DCCV_REDNETFAMFONTERED_9'
In [28]:
exr_msg = istat.dataflow(cod)
exr_flow = exr_msg.dataflow[cod]
dsd = exr_flow.structure

dims = dict()
for dimension in dsd.dimensions:
    if dimension != 'TIME_PERIOD':
        dims[dimension] = sdmx.to_pandas(dsd.dimensions.get(dimension).local_representation.enumerated).to_dict()['name']
2024-09-18 18:09:06,242 pandasdmx.reader.sdmxml - DEBUG: Truncate sub-microsecond time in <Prepared>
In [29]:
dims.keys()
Out[29]:
dict_keys([<Dimension FREQ>, <Dimension REF_AREA>, <Dimension DATA_TYPE>, <Dimension MEASURE>, <Dimension IMPUTED_RENTS>, <Dimension FAM_MAIN_INCOME_SOURCE>, <Dimension NUMBER_HOUSEHOLD_COMP>, <Dimension HOUSEHOLD_TYPOLOGY>, <Dimension NUMB_OF_CHILDREN>, <Dimension NUMB_OF_ELDERLY>, <Dimension SEX_MAIN_PERCEPTOR>, <Dimension AGE_MAIN_EARNIER>, <Dimension EDU_LEV_MAIN_EARN>, <Dimension LABPROF_STATUS_C_MAIN_EARNER>])
In [30]:
dims['FAM_MAIN_INCOME_SOURCE']
Out[30]:
{'1': 'employee income',
 '2': 'self-employed income',
 '3': 'public transfers income',
 '4': 'other type',
 '9': 'total'}
In [31]:
dims['IMPUTED_RENTS']
Out[31]:
{'1': 'including imputed rents', '2': 'not including imputed rents'}
In [32]:
dims['DATA_TYPE']
Out[32]:
{'FAMIGLIE_POV': 'Poor households',
 'INCID_POVASS_FAM': 'household absolute poverty incidence (% of households in absolute poverty)',
 'FAM_POVASS': 'households in absolute poverty (%  composition by residence area)',
 'INTENS_POVASS_FAM': 'household absolute poverty intensity (% difference by the poverty threshold)',
 'FAM_POVASS_VAL': 'households in absolute poverty (thousands)',
 'FAM_VAL_PERC_POV': 'households by poverty line (% composition)',
 'INCID_POVREL_FAM': 'household relative poverty incidence (% of households in relative poverty)',
 'FAM_POVREL': 'households in relative poverty (% composition by residence area)',
 'INTENS_POVREL_FAM': 'household relative poverty intensity (difference in % by the poverty threshold)',
 'FAM_POVREL_VAL': 'households in relative poverty (thousands)',
 'POV_ASSOLUTA': 'Absolute poverty',
 'FAM_VAL_PERC': 'households (% composition)',
 'FAM_VAL_PERC_QUA': 'households per  expenditure quantile (% composition)',
 'INDIVIDUI_POV': 'Poor individuals',
 'INCID_POVASS_INDIV': 'individual absolute poverty incidence (% of persons living in households in absolute poverty)',
 'INDIV_POVASS': 'individuals in absolute poverty (% composition by resident area)',
 'INDIV_POVASS_VAL': 'individuals in absolute poverty (thousands)',
 'INCID_POVREL_INDIV': 'individual relative poverty incidence (% of persons living in households in relative poverty)',
 'INDIV_POVREL': 'individuals in relative poverty (% composition by resident area)',
 'INDIV_POVREL_VAL': 'individuals in relative poverty (thousands)',
 'POV_RELATIVA': 'Relative poverty',
 'SOGLIA_POVREL': 'relative poverty threshold (monthly expenditure in euros)',
 'SOGLIA_POVASS': 'absolute poverty threshold',
 'DISUG_CONSUMI_GINI': 'Gini concentration index',
 'DISUG_CONSUMI_ID': 's80/s20 quintile share ratio (average values)',
 'FAM_COMPORT': 'households purchasing behaviour',
 'FAM_LUOGHI': 'households purchasing place',
 'FAM_SPEFF_ABB': 'households with expenditures for clothing and footwear',
 'FAM_SPEFF_ABITAZ': 'households with expenditures for housing services',
 'FAM_SPEFF_BENDUR': 'households with expenditures for durable goods',
 'FAM_SPEFF_GIORN': 'households with expenditures for newspaper and books',
 'FAM_SPEFF_PASTI': 'households with expenditures for catering services',
 'FAM_SPEFF_SANIT': 'households with expenditures for health services',
 'FAM_SPEFF_TRASPRIV': 'households with expenditures for private transport',
 'FAM_SPEFF_TRASPUBB': 'households with expenditures for public transport',
 'SPESA_EFF_ABB': 'households reporting expenditures on clothing and footwear',
 'SPESA_EFF_ABITAZ': 'households reporting expenditures on housing services',
 'SPESA_EFF_BENDUR': 'households reporting expenditures on durable goods',
 'SPESA_EFF_FAM': 'households reporting expenditures on some items',
 'SPESA_EFF_GIORN': 'households reporting expenditures on newspaper and books',
 'SPESA_EFF_PASTI': 'households reporting expenditures on catering services',
 'SPESA_EFF_SANIT': 'households reporting expenditures on health services',
 'SPESA_EFF_TRASPRIV': 'households reporting expenditures on private transport',
 'SPESA_EFF_TRASPUBB': 'households reporting expenditures on public transport',
 'SPESA_MEDIA': 'household average monthly expenditure (in current euros)',
 'SPESA_QUINTO': 'household expenditure quintile',
 'ABITAZ_AFFOLL_MED': 'household crowding index (number of components of household per square meter)',
 'ABITAZ_PROBLEMI': 'households with problems with the housing (percentage of household with the same characteristics)',
 'ABITAZ_SPESA_M': 'housing average monthly expenditures (in euros)',
 'ABITAZ_SPESA_REDD': 'housing average monthly expenditures (percentage of average monthly income)',
 'DISUG_REDDNET_GINI': 'net family income distribution homogeneity',
 'FAM_ARR_SPESA': 'households who have arrears on selected items (percent of households that  bought that item)',
 'FAM_FONTE_REDD': 'household per main income source (percentage values)',
 'FAM_GIUD_CARICO': 'households who believe some housing expenses are burden (percentage of households facing expenses)',
 'FAM_GIUD_FINEMESE': 'households per minimun income considered enough to make ends meet (percentage composition)',
 'FAM_GIUD_PERCEP': "households who can and don't afford to arrive at the end of the month (percentage composition)",
 'FAM_NO_BENDUR': 'households who cannot afford some durable goods (percent of households)',
 'FAM_NO_SOLDI': 'households who do not have enough money for selected items (percent of households)',
 'FAM_RISP': "households that can't afford to save (percent of households)",
 'REDD_MEDIANO_FAM': 'annual median households income',
 'REDD_MEDIO_FAM': 'annual average households income',
 'REDD_QUINTO': 'households per fifth of equivalent household income (per 100 households with same characteristics)',
 'FAM_SPESE_IMPR': "households that can't face unexpected expenses (percent of households)",
 'FAM_NO_PERMETTERSI': 'households who do not afford to buy selected items (percent of household)',
 'ZONA_RES_PROBLEMI': 'problems in the dwelling place',
 'FAM_TITOLO_GODIM_ABITAZ': 'tenure status',
 'REDD_QUINTO_STESSO': 'households per fifth of equivalent household income (per 100 households who belong to the same fifth of equivalent household income)',
 'PCT_POV_ESCL': 'People at risk of poverty or social exclusion',
 'PCT_RISK_POV': 'People at risk of poverty',
 'PCT_SEV_MAT_DEP': 'People in conditions of severe material deprivation (4 out of 9 symptoms)',
 'PCT_MAT_DEP': 'People in conditions of material deprivation (3 out of 9 symptoms)',
 'PCT_LOW_INT': 'People in households with very low work intensity',
 'PCT_POV_SOC_EXCL_EU2030': 'People at risk of poverty or social exclusion - Europe 2030',
 'PCT_SEV_MAT_SOC_DEP_MOD13_EU2030': 'People in conditions of severe material and social deprivation (7 out of 13 symptoms) - Europe 2030',
 'PCT_MAT_SOC_DEP_MOD13_EU2030': 'People in conditions of material e social deprivation (5 out of 13 symptoms) - Europe 2030',
 'PCT_LOW_WORK_INT_EU2030': 'People in households with very low work intensity - Europa 2030'}
In [33]:
# data_msg = istat.data(cod)
# data_df = pd.DataFrame(sdmx.to_pandas(data_msg)).reset_index()
In [34]:
key = {
    'DATA_TYPE': 'REDD_MEDIO_FAM',
    'IMPUTED_RENTS': '2',
    'FAM_MAIN_INCOME_SOURCE': '9'
}
params = {
    'startPeriod':'2003'
}
try:
    data_msg = istat.data(cod,key=key,params=params)
    data_df = pd.DataFrame(sdmx.to_pandas(data_msg)).reset_index()
except Exception as err:
    print(err)

for field in dims.keys():
    data_df[field] = data_df[field].map(dims[field])
2024-09-18 18:09:08,466 pandasdmx.reader.sdmxml - DEBUG: Truncate sub-microsecond time in <Prepared>
2024-09-18 18:09:12,503 pandasdmx.reader.sdmxml - INFO: Use supplied dsd=… argument for non–structure-specific message
In [35]:
data_df = data_df[data_df['REF_AREA'].isin(['metropolitan area - centre',
        'Italy',  'Lazio'])].copy()
data_df = data_df[['REF_AREA','TIME_PERIOD','value']].copy()
base_df = data_df[data_df['TIME_PERIOD']=='2003'].copy()
m = data_df.merge(base_df, on='REF_AREA', how='left')
m['v'] = m['value_x'] / m['value_y'] * 100
m['TIME_PERIOD'] = m['TIME_PERIOD_x'].astype(int)
In [36]:
layout = go.Layout(title='Reddito medio (Irpef) base 2003')
fig = go.Figure(layout=layout)

for ref in list(m['REF_AREA'].unique()):
    temp = m[m['REF_AREA']==ref].sort_values(by='TIME_PERIOD').copy()
    fig.add_trace(go.Scatter(x=temp['TIME_PERIOD'], y=temp['v'], name=ref,mode='lines+markers',marker=dict(size=5)))
fig.show()
In [ ]:
 

Famiglie in affitto per classe di reddito¶

In [37]:
#dataflows[(dataflows['DESCR_IT'].str.contains('affitto', case=False))].to_dict(orient='records')
In [38]:
#dataflows[(dataflows['COD'].str.contains('33_290', case=False))].to_dict(orient='records')
In [39]:
cod = '33_290_DF_DCCV_TITGODABIT_5'
In [40]:
exr_msg = istat.dataflow(cod)
exr_flow = exr_msg.dataflow[cod]
dsd = exr_flow.structure

dims = dict()
for dimension in dsd.dimensions:
    if dimension != 'TIME_PERIOD':
        dims[dimension] = sdmx.to_pandas(dsd.dimensions.get(dimension).local_representation.enumerated).to_dict()['name']
2024-09-18 18:09:18,835 pandasdmx.reader.sdmxml - DEBUG: Truncate sub-microsecond time in <Prepared>
In [41]:
dims.keys()
Out[41]:
dict_keys([<Dimension FREQ>, <Dimension REF_AREA>, <Dimension DATA_TYPE>, <Dimension MEASURE>, <Dimension TENURE_STATUS>, <Dimension FIFTH_EQUIV_HOUSE_INC>])
In [42]:
key = {
    'TENURE_STATUS': '1'
}
params = {
    'startPeriod':'2003'
}
try:
    data_msg = istat.data(cod,key=key,params=params)
    data_df = pd.DataFrame(sdmx.to_pandas(data_msg)).reset_index()
except Exception as err:
    print(err)

for field in dims.keys():
    data_df[field] = data_df[field].map(dims[field])
2024-09-18 18:09:21,434 pandasdmx.reader.sdmxml - DEBUG: Truncate sub-microsecond time in <Prepared>
2024-09-18 18:09:24,609 pandasdmx.reader.sdmxml - INFO: Use supplied dsd=… argument for non–structure-specific message
In [43]:
data_df = data_df[['FIFTH_EQUIV_HOUSE_INC','TIME_PERIOD','value']].copy()
In [44]:
data_df = data_df[data_df['FIFTH_EQUIV_HOUSE_INC']!='total'].copy()
In [45]:
data_df
Out[45]:
FIFTH_EQUIV_HOUSE_INC TIME_PERIOD value
0 first(lowest) 2004 35.5
1 first(lowest) 2005 35.0
2 first(lowest) 2006 37.2
3 first(lowest) 2007 36.0
4 first(lowest) 2008 37.9
... ... ... ...
95 fifth(highest) 2019 7.2
96 fifth(highest) 2020 5.6
97 fifth(highest) 2021 7.1
98 fifth(highest) 2022 5.2
99 fifth(highest) 2023 5.3

100 rows × 3 columns

In [46]:
data_df['FIFTH_EQUIV_HOUSE_INC'].unique()
Out[46]:
array(['first(lowest)', 'second', 'third', 'fourth', 'fifth(highest)'],
      dtype=object)
In [47]:
bars = []

for cls in data_df['FIFTH_EQUIV_HOUSE_INC'].unique():
    temp = data_df[data_df['FIFTH_EQUIV_HOUSE_INC']==cls].copy()
    bars.append(go.Bar(name=cls, x=temp['TIME_PERIOD'], y=temp['value']))

fig = go.Figure(data=bars)

# Change the bar mode
fig.update_layout(barmode='group')
fig.show()
In [ ]: