io

Saving/loading data

Directory where the data (XML files) are stored

directory = pathlib.Path.cwd().parent / 'samples'
assert directory.exists()
directory
PosixPath('/home/manu/Sync/UC3M/proyectos/2022/nextProcurement/sproc/samples')

A (sample) file in that directory

xml_file = directory / 'PlataformasAgregadasSinMenores_20220104_030016_1.atom'
assert xml_file.exists()
xml_file
PosixPath('/home/manu/Sync/UC3M/proyectos/2022/nextProcurement/sproc/samples/PlataformasAgregadasSinMenores_20220104_030016_1.atom')
df = sproc.xml.to_df(xml_file)
df.head(6)
id summary title updated ContractFolderStatus - ContractFolderID ContractFolderStatus - ContractFolderStatusCode ContractFolderStatus - LocatedContractingParty - BuyerProfileURIID ContractFolderStatus - LocatedContractingParty - Party - PartyName - Name ContractFolderStatus - LocatedContractingParty - ParentLocatedParty - PartyName - Name ContractFolderStatus - ProcurementProject - Name ... ContractFolderStatus - LegalDocumentReference - Attachment - ExternalReference - URI ContractFolderStatus - TechnicalDocumentReference - ID ContractFolderStatus - TechnicalDocumentReference - Attachment - ExternalReference - URI ContractFolderStatus - ProcurementProject - PlannedPeriod - StartDate ContractFolderStatus - ProcurementProject - PlannedPeriod - EndDate ContractFolderStatus - LocatedContractingParty - Party - PartyIdentification - ID ContractFolderStatus - LocatedContractingParty - ParentLocatedParty - ParentLocatedParty - PartyName - Name ContractFolderStatus - TenderingProcess - ParticipationRequestReceptionPeriod - EndDate ContractFolderStatus - TenderingProcess - ParticipationRequestReceptionPeriod - EndTime ContractFolderStatus - TenderResult - AwardedTenderedProject - ProcurementProjectLotID
0 https://contrataciondelestado.es/sindicacion/P... Id licitación: C. 2-2021; Órgano de Contrataci... L'objecte del contracte és la renovació de tot... 2022-01-03T01:11:41.826+01:00 C. 2-2021 ADJ https://contractaciopublica.gencat.cat/ecofin_... Ajuntament de Sant Ramon Entitats municipals de Catalunya L'objecte del contracte és la renovació de tot... ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 https://contrataciondelestado.es/sindicacion/P... Id licitación: 8128_3/2021; Órgano de Contrata... Obras de restauración hidromorfológica del río... 2022-01-03T01:00:11.194+01:00 8128_3/2021 PUB NaN Pleno del Ayuntamiento AYUNTAMIENTO DE MONREAL Obras de restauración hidromorfológica del río... ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 https://contrataciondelestado.es/sindicacion/P... Id licitación: 1000_0005-CP01-2021-000063; Órg... Contrato del servicio de realización de labore... 2022-01-03T01:00:10.399+01:00 1000_0005-CP01-2021-000063 EV NaN El Director General de Comunicación y Relacion... Departamento de Presidencia, Igualdad, Función... Contrato del servicio de realización de labore... ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 https://contrataciondelestado.es/sindicacion/P... Id licitación: 1379/2020 4738; Órgano de Contr... Obres de renovació de l'enllumenat públic a la... 2022-01-03T00:11:40.740+01:00 1379/2020 4738 EV https://contractaciopublica.gencat.cat/ecofin_... Ajuntament de Canet de Mar Entitats municipals de Catalunya Obres de renovació de l'enllumenat públic a la... ... https://contractaciopublica.gencat.cat/ecofin_... NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 https://contrataciondelestado.es/sindicacion/P... Id licitación: 2021-44; Órgano de Contratación... Subministre i la instal·lació fotovoltaica en ... 2022-01-03T00:11:40.696+01:00 2021-44 EV https://contractaciopublica.gencat.cat/ecofin_... Ajuntament de Valls Entitats municipals de Catalunya Subministre i la instal·lació fotovoltaica en ... ... https://contractaciopublica.gencat.cat/ecofin_... Enllac plec clausules tecniques.doc https://contractaciopublica.gencat.cat/ecofin_... NaN NaN NaN NaN NaN NaN NaN
5 https://contrataciondelestado.es/sindicacion/P... Id licitación: 2809/2021; Órgano de Contrataci... Servei de visites i activitats sobre Història ... 2022-01-03T00:11:40.639+01:00 2809/2021 EV https://contractaciopublica.gencat.cat/ecofin_... Institut de Cultura de Barcelona Entitats municipals de Catalunya Servei de visites i activitats sobre Història ... ... https://contractaciopublica.gencat.cat/ecofin_... 2809-21 PPT.pdf https://contractaciopublica.gencat.cat/ecofin_... NaN NaN NaN NaN NaN NaN NaN

6 rows × 38 columns

Below, it is required that no extension is provided for the file

file = pathlib.Path('data.txt')
file.suffix == ''
False
file = pathlib.Path('data')
file.suffix == ''
True

A class to handle reading and writing a pd.DataFrame


source

File

 File (stem:str|pathlib.Path)

Initialize self. See help(type(self)) for accurate signature.

try:
    File('tmp.feather')
except AssertionError:
    print('oooooops')
oooooops
file = File('tmp')
file
tmp.pickle

Does the file already exists?

file.exists()
False

Saving

Dealing with multivalued columns

A shorter version of the pd.DataFrame

head_df = df.head(5).copy()
head_df
id summary title updated ContractFolderStatus - ContractFolderID ContractFolderStatus - ContractFolderStatusCode ContractFolderStatus - LocatedContractingParty - BuyerProfileURIID ContractFolderStatus - LocatedContractingParty - Party - PartyName - Name ContractFolderStatus - LocatedContractingParty - ParentLocatedParty - PartyName - Name ContractFolderStatus - ProcurementProject - Name ... ContractFolderStatus - LegalDocumentReference - Attachment - ExternalReference - URI ContractFolderStatus - TechnicalDocumentReference - ID ContractFolderStatus - TechnicalDocumentReference - Attachment - ExternalReference - URI ContractFolderStatus - ProcurementProject - PlannedPeriod - StartDate ContractFolderStatus - ProcurementProject - PlannedPeriod - EndDate ContractFolderStatus - LocatedContractingParty - Party - PartyIdentification - ID ContractFolderStatus - LocatedContractingParty - ParentLocatedParty - ParentLocatedParty - PartyName - Name ContractFolderStatus - TenderingProcess - ParticipationRequestReceptionPeriod - EndDate ContractFolderStatus - TenderingProcess - ParticipationRequestReceptionPeriod - EndTime ContractFolderStatus - TenderResult - AwardedTenderedProject - ProcurementProjectLotID
0 https://contrataciondelestado.es/sindicacion/P... Id licitación: C. 2-2021; Órgano de Contrataci... L'objecte del contracte és la renovació de tot... 2022-01-03T01:11:41.826+01:00 C. 2-2021 ADJ https://contractaciopublica.gencat.cat/ecofin_... Ajuntament de Sant Ramon Entitats municipals de Catalunya L'objecte del contracte és la renovació de tot... ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 https://contrataciondelestado.es/sindicacion/P... Id licitación: 8128_3/2021; Órgano de Contrata... Obras de restauración hidromorfológica del río... 2022-01-03T01:00:11.194+01:00 8128_3/2021 PUB NaN Pleno del Ayuntamiento AYUNTAMIENTO DE MONREAL Obras de restauración hidromorfológica del río... ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 https://contrataciondelestado.es/sindicacion/P... Id licitación: 1000_0005-CP01-2021-000063; Órg... Contrato del servicio de realización de labore... 2022-01-03T01:00:10.399+01:00 1000_0005-CP01-2021-000063 EV NaN El Director General de Comunicación y Relacion... Departamento de Presidencia, Igualdad, Función... Contrato del servicio de realización de labore... ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 https://contrataciondelestado.es/sindicacion/P... Id licitación: 1379/2020 4738; Órgano de Contr... Obres de renovació de l'enllumenat públic a la... 2022-01-03T00:11:40.740+01:00 1379/2020 4738 EV https://contractaciopublica.gencat.cat/ecofin_... Ajuntament de Canet de Mar Entitats municipals de Catalunya Obres de renovació de l'enllumenat públic a la... ... https://contractaciopublica.gencat.cat/ecofin_... NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 https://contrataciondelestado.es/sindicacion/P... Id licitación: 2021-44; Órgano de Contratación... Subministre i la instal·lació fotovoltaica en ... 2022-01-03T00:11:40.696+01:00 2021-44 EV https://contractaciopublica.gencat.cat/ecofin_... Ajuntament de Valls Entitats municipals de Catalunya Subministre i la instal·lació fotovoltaica en ... ... https://contractaciopublica.gencat.cat/ecofin_... Enllac plec clausules tecniques.doc https://contractaciopublica.gencat.cat/ecofin_... NaN NaN NaN NaN NaN NaN NaN

5 rows × 38 columns

There are multivalued columns in the pd.DataFrame

multivalued_columns = sproc.structure.multivalued_columns(head_df)
multivalued_columns
['ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode',
 'ContractFolderStatus - ValidNoticeInfo - AdditionalPublicationStatus - PublicationMediaName',
 'ContractFolderStatus - ValidNoticeInfo - AdditionalPublicationStatus - AdditionalPublicationDocumentReference - IssueDate']
head_df[multivalued_columns]
ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode ContractFolderStatus - ValidNoticeInfo - AdditionalPublicationStatus - PublicationMediaName ContractFolderStatus - ValidNoticeInfo - AdditionalPublicationStatus - AdditionalPublicationDocumentReference - IssueDate
0 [[DOC_CN, DOC_CAN_ADJ]] [[Perfil del contratante, Perfil del contratan... [[2021-11-30, 2022-01-03]]
1 DOC_CN Perfil del contratante 2022-01-03
2 DOC_CN [[DOUE, Perfil del contratante]] [[2021-12-01, 2022-01-03]]
3 DOC_CN Perfil del contratante 2021-12-13
4 DOC_CN Perfil del contratante 2021-12-17

A convenience function to turn something into a list if not already one.


source

cast_to_list_if_not_already

 cast_to_list_if_not_already (x:Any)

Casts an element to list type if not already

Type Details
x Any Input
Returns list | numpy.ndarray Output
cast_to_list_if_not_already(2)
[2]
cast_to_list_if_not_already([2])
[2]
cast_to_list_if_not_already(np.array([2]))
array([2])

A function to homegenize multivalued columns so that every element is a list (maybe containing a single element).


source

homogenize_multivalued

 homogenize_multivalued (df:pandas.core.frame.DataFrame)

Processes every column containing a list such that every* element is a list*

Type Details
df DataFrame Input
Returns DataFrame Output
homogenized_head_df = homogenize_multivalued(head_df)
homogenized_head_df
id summary title updated ContractFolderStatus - ContractFolderID ContractFolderStatus - ContractFolderStatusCode ContractFolderStatus - LocatedContractingParty - BuyerProfileURIID ContractFolderStatus - LocatedContractingParty - Party - PartyName - Name ContractFolderStatus - LocatedContractingParty - ParentLocatedParty - PartyName - Name ContractFolderStatus - ProcurementProject - Name ... ContractFolderStatus - LegalDocumentReference - Attachment - ExternalReference - URI ContractFolderStatus - TechnicalDocumentReference - ID ContractFolderStatus - TechnicalDocumentReference - Attachment - ExternalReference - URI ContractFolderStatus - ProcurementProject - PlannedPeriod - StartDate ContractFolderStatus - ProcurementProject - PlannedPeriod - EndDate ContractFolderStatus - LocatedContractingParty - Party - PartyIdentification - ID ContractFolderStatus - LocatedContractingParty - ParentLocatedParty - ParentLocatedParty - PartyName - Name ContractFolderStatus - TenderingProcess - ParticipationRequestReceptionPeriod - EndDate ContractFolderStatus - TenderingProcess - ParticipationRequestReceptionPeriod - EndTime ContractFolderStatus - TenderResult - AwardedTenderedProject - ProcurementProjectLotID
0 https://contrataciondelestado.es/sindicacion/P... Id licitación: C. 2-2021; Órgano de Contrataci... L'objecte del contracte és la renovació de tot... 2022-01-03T01:11:41.826+01:00 C. 2-2021 ADJ https://contractaciopublica.gencat.cat/ecofin_... Ajuntament de Sant Ramon Entitats municipals de Catalunya L'objecte del contracte és la renovació de tot... ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 https://contrataciondelestado.es/sindicacion/P... Id licitación: 8128_3/2021; Órgano de Contrata... Obras de restauración hidromorfológica del río... 2022-01-03T01:00:11.194+01:00 8128_3/2021 PUB NaN Pleno del Ayuntamiento AYUNTAMIENTO DE MONREAL Obras de restauración hidromorfológica del río... ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 https://contrataciondelestado.es/sindicacion/P... Id licitación: 1000_0005-CP01-2021-000063; Órg... Contrato del servicio de realización de labore... 2022-01-03T01:00:10.399+01:00 1000_0005-CP01-2021-000063 EV NaN El Director General de Comunicación y Relacion... Departamento de Presidencia, Igualdad, Función... Contrato del servicio de realización de labore... ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 https://contrataciondelestado.es/sindicacion/P... Id licitación: 1379/2020 4738; Órgano de Contr... Obres de renovació de l'enllumenat públic a la... 2022-01-03T00:11:40.740+01:00 1379/2020 4738 EV https://contractaciopublica.gencat.cat/ecofin_... Ajuntament de Canet de Mar Entitats municipals de Catalunya Obres de renovació de l'enllumenat públic a la... ... https://contractaciopublica.gencat.cat/ecofin_... NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 https://contrataciondelestado.es/sindicacion/P... Id licitación: 2021-44; Órgano de Contratación... Subministre i la instal·lació fotovoltaica en ... 2022-01-03T00:11:40.696+01:00 2021-44 EV https://contractaciopublica.gencat.cat/ecofin_... Ajuntament de Valls Entitats municipals de Catalunya Subministre i la instal·lació fotovoltaica en ... ... https://contractaciopublica.gencat.cat/ecofin_... Enllac plec clausules tecniques.doc https://contractaciopublica.gencat.cat/ecofin_... NaN NaN NaN NaN NaN NaN NaN

5 rows × 38 columns

Notice the affected columns are still multivalued

assert sproc.structure.multivalued_columns(homogenized_head_df) == multivalued_columns

A function to try and cast all the elements in a list to float falling back to str when any single element fails


source

cast_list_to_floats_or_strs

 cast_list_to_floats_or_strs (l:list)

Homogenizes the elements in a list such that they are all either float or str

Type Details
l list Input
Returns list Output

Conversion to floats is fine

cast_list_to_floats_or_strs([np.NAN, '14.1'])
[nan, 14.1]

Conversion to float is not possible due to the newly added last element

cast_list_to_floats_or_strs([np.NAN, '14.1', 'hola'])
['nan', '14.1', 'hola']

PandasNA are a special case

cast_list_to_floats_or_strs([1.0, 2., pd.NA])
[1.0, 2.0, nan]

When nested lists are found, every element (either scalar or list) is converted to str

converted = cast_list_to_floats_or_strs([3.14, [1, 2]])
converted
['3.14', '[1, 2]']

The list was also turned into a str

type(converted[0])
str

For the sake of example, a fake float value is added to the first element, which until now consisted only of strs.

# homogenized_head_df.loc[0, 'ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode'].append(13.5)
homogenized_head_df.loc[0, 'ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode'].append([13.5])

On the other hand, the second element is turned into float-only list.

homogenized_head_df.loc[1, 'ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode'] = [3.14]
homogenized_head_df['ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode']
0    [[DOC_CN, DOC_CAN_ADJ], [13.5]]
1                             [3.14]
2                           [DOC_CN]
3                           [DOC_CN]
4                           [DOC_CN]
Name: ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode, dtype: object

All the individual lists (a row in this column or pd.Series) are homogenized (all the values cast to the either float or str)

cast_homogenized_head_df = homogenized_head_df[multivalued_columns].applymap(cast_list_to_floats_or_strs)
cast_homogenized_head_df
ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode ContractFolderStatus - ValidNoticeInfo - AdditionalPublicationStatus - PublicationMediaName ContractFolderStatus - ValidNoticeInfo - AdditionalPublicationStatus - AdditionalPublicationDocumentReference - IssueDate
0 [['DOC_CN', 'DOC_CAN_ADJ'], [13.5]] [['Perfil del contratante', 'Perfil del contra... [['2021-11-30', '2022-01-03']]
1 [3.14] [Perfil del contratante] [2022-01-03]
2 [DOC_CN] [['DOUE', 'Perfil del contratante']] [['2021-12-01', '2022-01-03']]
3 [DOC_CN] [Perfil del contratante] [2021-12-13]
4 [DOC_CN] [Perfil del contratante] [2021-12-17]

However, the types across lists are not necessary the same

cast_homogenized_head_df['ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode'].apply(lambda x: type(x[0]))
0      <class 'str'>
1    <class 'float'>
2      <class 'str'>
3      <class 'str'>
4      <class 'str'>
Name: ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode, dtype: object

A function to cast all the lists to a common type.


source

cast_multivalued_series_to_common_type

 cast_multivalued_series_to_common_type (s:pandas.core.series.Series)

**Casts* all the lists in the input series to a common type*

Type Details
s Series Input
Returns Series Output

We use the cast version (with homogeneous lists)

# cast_homogenized_head_df.loc[0, 'ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode']
cast_homogenized_head_df['ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode']
0    [['DOC_CN', 'DOC_CAN_ADJ'], [13.5]]
1                                 [3.14]
2                               [DOC_CN]
3                               [DOC_CN]
4                               [DOC_CN]
Name: ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode, dtype: object
# common_type_homogenized_head_df = cast_multivalued_series_to_common_type(homogenized_head_df['ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode']) # <-------
common_type_homogenized_head_df = cast_multivalued_series_to_common_type(cast_homogenized_head_df['ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode'])
common_type_homogenized_head_df
0    [['DOC_CN', 'DOC_CAN_ADJ'], [13.5]]
1                                 [3.14]
2                               [DOC_CN]
3                               [DOC_CN]
4                               [DOC_CN]
Name: ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode, dtype: object
common_type_homogenized_head_df.apply(lambda x: type(x[0]))
0    <class 'str'>
1    <class 'str'>
2    <class 'str'>
3    <class 'str'>
4    <class 'str'>
Name: ContractFolderStatus - ValidNoticeInfo - NoticeTypeCode, dtype: object

Writing

A method to write a pd.DataFrame to disk


source

show_doc

 show_doc (sym, renderer=None, name:str|None=None, title_level:int=3)

Show signature and docstring for sym

Type Default Details
sym Symbol to document
renderer NoneType None Optional renderer (defaults to markdown)
name str | None None Optionally override displayed name of sym
title_level int 3 Heading level to use for symbol name

File is written

file.write(df)

Does it exist?

file.exists()
True

Loading


source

show_doc

 show_doc (sym, renderer=None, name:str|None=None, title_level:int=3)

Show signature and docstring for sym

Type Default Details
sym Symbol to document
renderer NoneType None Optional renderer (defaults to markdown)
name str | None None Optionally override displayed name of sym
title_level int 3 Heading level to use for symbol name
loaded_df = file.read()
loaded_df.head()
id summary title updated ContractFolderStatus - ContractFolderID ContractFolderStatus - ContractFolderStatusCode ContractFolderStatus - LocatedContractingParty - BuyerProfileURIID ContractFolderStatus - LocatedContractingParty - Party - PartyName - Name ContractFolderStatus - LocatedContractingParty - ParentLocatedParty - PartyName - Name ContractFolderStatus - ProcurementProject - Name ... ContractFolderStatus - LegalDocumentReference - Attachment - ExternalReference - URI ContractFolderStatus - TechnicalDocumentReference - ID ContractFolderStatus - TechnicalDocumentReference - Attachment - ExternalReference - URI ContractFolderStatus - ProcurementProject - PlannedPeriod - StartDate ContractFolderStatus - ProcurementProject - PlannedPeriod - EndDate ContractFolderStatus - LocatedContractingParty - Party - PartyIdentification - ID ContractFolderStatus - LocatedContractingParty - ParentLocatedParty - ParentLocatedParty - PartyName - Name ContractFolderStatus - TenderingProcess - ParticipationRequestReceptionPeriod - EndDate ContractFolderStatus - TenderingProcess - ParticipationRequestReceptionPeriod - EndTime ContractFolderStatus - TenderResult - AwardedTenderedProject - ProcurementProjectLotID
0 https://contrataciondelestado.es/sindicacion/P... Id licitación: C. 2-2021; Órgano de Contrataci... L'objecte del contracte és la renovació de tot... 2022-01-03T01:11:41.826+01:00 C. 2-2021 ADJ https://contractaciopublica.gencat.cat/ecofin_... Ajuntament de Sant Ramon Entitats municipals de Catalunya L'objecte del contracte és la renovació de tot... ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 https://contrataciondelestado.es/sindicacion/P... Id licitación: 8128_3/2021; Órgano de Contrata... Obras de restauración hidromorfológica del río... 2022-01-03T01:00:11.194+01:00 8128_3/2021 PUB NaN Pleno del Ayuntamiento AYUNTAMIENTO DE MONREAL Obras de restauración hidromorfológica del río... ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 https://contrataciondelestado.es/sindicacion/P... Id licitación: 1000_0005-CP01-2021-000063; Órg... Contrato del servicio de realización de labore... 2022-01-03T01:00:10.399+01:00 1000_0005-CP01-2021-000063 EV NaN El Director General de Comunicación y Relacion... Departamento de Presidencia, Igualdad, Función... Contrato del servicio de realización de labore... ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 https://contrataciondelestado.es/sindicacion/P... Id licitación: 1379/2020 4738; Órgano de Contr... Obres de renovació de l'enllumenat públic a la... 2022-01-03T00:11:40.740+01:00 1379/2020 4738 EV https://contractaciopublica.gencat.cat/ecofin_... Ajuntament de Canet de Mar Entitats municipals de Catalunya Obres de renovació de l'enllumenat públic a la... ... https://contractaciopublica.gencat.cat/ecofin_... NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 https://contrataciondelestado.es/sindicacion/P... Id licitación: 2021-44; Órgano de Contratación... Subministre i la instal·lació fotovoltaica en ... 2022-01-03T00:11:40.696+01:00 2021-44 EV https://contractaciopublica.gencat.cat/ecofin_... Ajuntament de Valls Entitats municipals de Catalunya Subministre i la instal·lació fotovoltaica en ... ... https://contractaciopublica.gencat.cat/ecofin_... Enllac plec clausules tecniques.doc https://contractaciopublica.gencat.cat/ecofin_... NaN NaN NaN NaN NaN NaN NaN

5 rows × 38 columns

Saved and loaded pd.DataFrames should be equal

assert df.equals(loaded_df)
file.name.unlink()