From 96ea65bb1302162928968be6f55e93d31bbfb87c Mon Sep 17 00:00:00 2001 From: Alix Damman Date: Tue, 19 Jun 2018 16:38:39 +0200 Subject: [PATCH 1/3] updated and added example files in larray/tests/data + added get_example_filepath() function --- doc/source/api.rst | 1 + larray/example.py | 31 +++- larray/inout/csv.py | 141 ++++++++++-------- larray/inout/excel.py | 141 +++++++++++------- larray/inout/hdf.py | 44 ++---- larray/tests/data/births_and_deaths.xlsx | Bin 0 -> 9747 bytes larray/tests/data/examples.h5 | Bin 0 -> 23112 bytes larray/tests/data/examples.xlsx | Bin 12653 -> 13313 bytes larray/tests/data/examples/births.csv | 7 + larray/tests/data/examples/deaths.csv | 7 + larray/tests/data/examples/pop.csv | 7 + .../data/examples/pop_missing_axis_name.csv | 7 + .../data/examples/pop_missing_values.csv | 5 + .../tests/data/examples/pop_narrow_format.csv | 7 + larray/tests/data/missing_axis_name.csv | 9 +- larray/tests/data/pop_only.xlsx | Bin 0 -> 10282 bytes larray/tests/data/population_session.h5 | Bin 0 -> 3190144 bytes larray/tests/data/population_session.xlsx | Bin 0 -> 12045 bytes .../data/population_session/__axes__.csv | 4 + .../data/population_session/__groups__.csv | 3 + .../tests/data/population_session/births.csv | 7 + .../tests/data/population_session/deaths.csv | 7 + larray/tests/data/population_session/pop.csv | 7 + larray/tests/data/test.xlsx | Bin 13436 -> 15265 bytes 24 files changed, 278 insertions(+), 157 deletions(-) create mode 100644 larray/tests/data/births_and_deaths.xlsx create mode 100644 larray/tests/data/examples.h5 create mode 100644 larray/tests/data/examples/births.csv create mode 100644 larray/tests/data/examples/deaths.csv create mode 100644 larray/tests/data/examples/pop.csv create mode 100644 larray/tests/data/examples/pop_missing_axis_name.csv create mode 100644 larray/tests/data/examples/pop_missing_values.csv create mode 100644 larray/tests/data/examples/pop_narrow_format.csv create mode 100644 larray/tests/data/pop_only.xlsx create mode 100644 larray/tests/data/population_session.h5 create mode 100644 larray/tests/data/population_session.xlsx create mode 100644 larray/tests/data/population_session/__axes__.csv create mode 100644 larray/tests/data/population_session/__groups__.csv create mode 100644 larray/tests/data/population_session/births.csv create mode 100644 larray/tests/data/population_session/deaths.csv create mode 100644 larray/tests/data/population_session/pop.csv diff --git a/doc/source/api.rst b/doc/source/api.rst index 8886c0cf4..9648c6e0d 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -537,6 +537,7 @@ Miscellaneous aslarray from_frame + get_example_filepath labels_array union stack diff --git a/larray/example.py b/larray/example.py index 340fc6836..3bf25c20e 100644 --- a/larray/example.py +++ b/larray/example.py @@ -1,12 +1,41 @@ import os import larray as la -__all__ = ['EXAMPLE_FILES_DIR', 'load_example_data'] +__all__ = ['get_example_filepath', 'load_example_data'] EXAMPLE_FILES_DIR = os.path.dirname(__file__) + '/tests/data/' AVAILABLE_EXAMPLE_DATA = { 'demography': os.path.join(EXAMPLE_FILES_DIR, 'demography.h5') } +AVAILABLE_EXAMPLE_FILES = os.listdir(EXAMPLE_FILES_DIR) + + +def get_example_filepath(fname): + """Return absolute path to an example file if exist. + + Parameters + ---------- + fname : str + Filename of an existing example file. + + Returns + ------- + Filepath + Absolute filepath to an example file if exists. + + Notes + ----- + A ValueError is raised if the provided filename does not represent an existing example file. + + Examples + -------- + >>> fpath = get_example_filepath('examples.xlsx') + """ + fpath = os.path.abspath(os.path.join(EXAMPLE_FILES_DIR, fname)) + if not os.path.exists(fpath): + raise ValueError("Example file {} does not exist. " + "Available example files are: {}".format(fname, AVAILABLE_EXAMPLE_FILES)) + return fpath def load_example_data(name): diff --git a/larray/inout/csv.py b/larray/inout/csv.py index 4c9161ab5..a1813ed0e 100644 --- a/larray/inout/csv.py +++ b/larray/inout/csv.py @@ -18,6 +18,7 @@ from larray.inout.session import register_file_handler from larray.inout.common import _get_index_col, FileHandler from larray.inout.pandas import df_aslarray, _axes_to_df, _df_to_axes, _groups_to_df, _df_to_groups +from larray.example import get_example_filepath __all__ = ['read_csv', 'read_tsv', 'read_eurostat'] @@ -32,12 +33,14 @@ def read_csv(filepath_or_buffer, nb_axes=None, index_col=None, sep=',', headerse Notes ----- csv file format: - arr,ages,sex,nat\time,1991,1992,1993 - A1,BI,H,BE,1,0,0 - A1,BI,H,FO,2,0,0 - A1,BI,F,BE,0,0,1 - A1,BI,F,FO,0,0,0 - A1,A0,H,BE,0,0,0 + + geo,gender\\time,2013,2014,2015 + Belgium,Male,5472856,5493792,5524068 + Belgium,Female,5665118,5687048,5713206 + France,Male,31772665,31936596,32175328 + France,Female,33827685,34005671,34280951 + Germany,Male,39380976,39556923,39835457 + Germany,Female,41142770,41210540,41362080 Parameters ---------- @@ -76,91 +79,105 @@ def read_csv(filepath_or_buffer, nb_axes=None, index_col=None, sep=',', headerse Examples -------- - >>> import os - >>> from larray import EXAMPLE_FILES_DIR - >>> fname = os.path.join(EXAMPLE_FILES_DIR, 'test2d.csv') + >>> csv_dir = get_example_filepath('examples') + >>> fname = csv_dir + '/pop.csv' + + >>> # The data below is derived from a subset of the demo_pjan table from Eurostat >>> read_csv(fname) - a\\b b0 b1 - 1 0 1 - 2 2 3 - 3 4 5 + geo gender\\time 2013 2014 2015 + Belgium Male 5472856 5493792 5524068 + Belgium Female 5665118 5687048 5713206 + France Male 31772665 31936596 32175328 + France Female 33827685 34005671 34280951 + Germany Male 39380976 39556923 39835457 + Germany Female 41142770 41210540 41362080 Missing label combinations - >>> fname = os.path.join(EXAMPLE_FILES_DIR, 'missing_values_3d.csv') + >>> fname = csv_dir + '/pop_missing_values.csv' >>> # let's take a look inside the CSV file. - >>> # they are missing label combinations: (a=2, b=b0) and (a=3, b=b1) + >>> # they are missing label combinations: (Paris, male) and (New York, female) >>> with open(fname) as f: ... print(f.read().strip()) - a,b\c,c0,c1,c2 - 1,b0,0,1,2 - 1,b1,3,4,5 - 2,b1,9,10,11 - 3,b0,12,13,14 + geo,gender\\time,2013,2014,2015 + Belgium,Male,5472856,5493792,5524068 + Belgium,Female,5665118,5687048,5713206 + France,Female,33827685,34005671,34280951 + Germany,Male,39380976,39556923,39835457 >>> # by default, cells associated with missing label combinations are filled with NaN. >>> # In that case, an int array is converted to a float array. >>> read_csv(fname) - a b\c c0 c1 c2 - 1 b0 0.0 1.0 2.0 - 1 b1 3.0 4.0 5.0 - 2 b0 nan nan nan - 2 b1 9.0 10.0 11.0 - 3 b0 12.0 13.0 14.0 - 3 b1 nan nan nan + geo gender\\time 2013 2014 2015 + Belgium Male 5472856.0 5493792.0 5524068.0 + Belgium Female 5665118.0 5687048.0 5713206.0 + France Male nan nan nan + France Female 33827685.0 34005671.0 34280951.0 + Germany Male 39380976.0 39556923.0 39835457.0 + Germany Female nan nan nan >>> # using argument 'fill_value', you can choose which value to use to fill missing cells. >>> read_csv(fname, fill_value=0) - a b\c c0 c1 c2 - 1 b0 0 1 2 - 1 b1 3 4 5 - 2 b0 0 0 0 - 2 b1 9 10 11 - 3 b0 12 13 14 - 3 b1 0 0 0 + geo gender\\time 2013 2014 2015 + Belgium Male 5472856 5493792 5524068 + Belgium Female 5665118 5687048 5713206 + France Male 0 0 0 + France Female 33827685 34005671 34280951 + Germany Male 39380976 39556923 39835457 + Germany Female 0 0 0 Specify the number of axes of the output array (useful when the name of the last axis is implicit) - >>> fname = os.path.join(EXAMPLE_FILES_DIR, 'missing_axis_name.csv') + >>> fname = csv_dir + '/pop_missing_axis_name.csv' >>> # let's take a look inside the CSV file. - >>> # The name of the second axis is missing. + >>> # The name of the last axis is missing. >>> with open(fname) as f: ... print(f.read().strip()) - a,b0,b1,b2 - a0,0,1,2 - a1,3,4,5 - a2,6,7,8 + geo,gender,2013,2014,2015 + Belgium,Male,5472856,5493792,5524068 + Belgium,Female,5665118,5687048,5713206 + France,Male,31772665,31936596,32175328 + France,Female,33827685,34005671,34280951 + Germany,Male,39380976,39556923,39835457 + Germany,Female,41142770,41210540,41362080 >>> # read the array stored in the CSV file as is - >>> read_csv(fname) - a\{1} b0 b1 b2 - a0 0 1 2 - a1 3 4 5 - a2 6 7 8 + >>> arr = read_csv(fname) + >>> # we expected a 3 x 2 x 3 array with data of type int + >>> # but we got a 6 x 4 array with data of type object + >>> arr.info + 6 x 4 + geo [6]: 'Belgium' 'Belgium' 'France' 'France' 'Germany' 'Germany' + {1} [4]: 'gender' '2013' '2014' '2015' + dtype: object + memory used: 192 bytes >>> # using argument 'nb_axes', you can force the number of axes of the output array - >>> read_csv(fname, nb_axes=2) - a\{1} b0 b1 b2 - a0 0 1 2 - a1 3 4 5 - a2 6 7 8 + >>> arr = read_csv(fname, nb_axes=3) + >>> # as expected, we have a 3 x 2 x 3 array with data of type int + >>> arr.info + 3 x 2 x 3 + geo [3]: 'Belgium' 'France' 'Germany' + gender [2]: 'Male' 'Female' + {2} [3]: 2013 2014 2015 + dtype: int64 + memory used: 144 bytes Read array saved in "narrow" format (wide=False) - >>> fname = os.path.join(EXAMPLE_FILES_DIR, 'narrow_2d.csv') + >>> fname = csv_dir + '/pop_narrow_format.csv' >>> # let's take a look inside the CSV file. >>> # Here, data are stored in a 'narrow' format. >>> with open(fname) as f: ... print(f.read().strip()) - a,b,value - 1,b0,0 - 1,b1,1 - 2,b0,2 - 2,b1,3 - 3,b0,4 - 3,b1,5 + geo,time,value + Belgium,2013,11137974 + Belgium,2014,11180840 + Belgium,2015,11237274 + France,2013,65600350 + France,2014,65942267 + France,2015,66456279 >>> # to read arrays stored in 'narrow' format, you must pass wide=False to read_csv >>> read_csv(fname, wide=False) - a\\b b0 b1 - 1 0 1 - 2 2 3 - 3 4 5 + geo\\time 2013 2014 2015 + Belgium 11137974 11180840 11237274 + France 65600350 65942267 66456279 """ if not np.isnan(na): fill_value = na diff --git a/larray/inout/excel.py b/larray/inout/excel.py index 1f1f2bf19..1801da04f 100644 --- a/larray/inout/excel.py +++ b/larray/inout/excel.py @@ -21,6 +21,8 @@ from larray.inout.common import _get_index_col, FileHandler from larray.inout.pandas import df_aslarray, _axes_to_df, _df_to_axes, _groups_to_df, _df_to_groups from larray.inout.xw_excel import open_excel +from larray.example import get_example_filepath + __all__ = ['read_excel'] @@ -69,89 +71,114 @@ def read_excel(filepath, sheet=0, nb_axes=None, index_col=None, fill_value=nan, Examples -------- - >>> import os - >>> from larray import EXAMPLE_FILES_DIR - >>> fname = os.path.join(EXAMPLE_FILES_DIR, 'examples.xlsx') + >>> fname = get_example_filepath('examples.xlsx') Read array from first sheet + >>> # The data below is derived from a subset of the demo_pjan table from Eurostat >>> read_excel(fname) - a a0 a1 a2 - 0 1 2 + geo gender\\time 2013 2014 2015 + Belgium Male 5472856 5493792 5524068 + Belgium Female 5665118 5687048 5713206 + France Male 31772665 31936596 32175328 + France Female 33827685 34005671 34280951 + Germany Male 39380976 39556923 39835457 + Germany Female 41142770 41210540 41362080 Read array from a specific sheet - >>> read_excel(fname, '2d') - a\\b b0 b1 - 1 0 1 - 2 2 3 - 3 4 5 + >>> # The data below is derived from a subset of the demo_fasec table from Eurostat + >>> read_excel(fname, 'births') + geo gender\\time 2013 2014 2015 + Belgium Male 64371 64173 62561 + Belgium Female 61235 60841 59713 + France Male 415762 418721 409145 + France Female 396581 400607 390526 + Germany Male 349820 366835 378478 + Germany Female 332249 348092 359097 Missing label combinations - >>> # let's take a look inside the sheet 'missing_values'. - >>> # they are missing label combinations: (a=2, b=b0) and (a=3, b=b1): + >>> # let's take a look inside the sheet 'pop_missing_values'. + >>> # they are missing label combinations: (Paris, male) and (New York, female): - a b\c c0 c1 c2 - 1 b0 0 1 2 - 1 b1 3 4 5 - 2 b1 9 10 11 - 3 b0 12 13 14 + geo gender\\time 2013 2014 2015 + Belgium Male 5472856 5493792 5524068 + Belgium Female 5665118 5687048 5713206 + France Female 33827685 34005671 34280951 + Germany Male 39380976 39556923 39835457 >>> # by default, cells associated with missing label combinations are filled with NaN. >>> # In that case, an int array is converted to a float array. - >>> read_excel(fname, sheet='missing_values') - a b\c c0 c1 c2 - 1 b0 0.0 1.0 2.0 - 1 b1 3.0 4.0 5.0 - 2 b0 nan nan nan - 2 b1 9.0 10.0 11.0 - 3 b0 12.0 13.0 14.0 - 3 b1 nan nan nan + >>> read_excel(fname, sheet='pop_missing_values') + geo gender\\time 2013 2014 2015 + Belgium Male 5472856.0 5493792.0 5524068.0 + Belgium Female 5665118.0 5687048.0 5713206.0 + France Male nan nan nan + France Female 33827685.0 34005671.0 34280951.0 + Germany Male 39380976.0 39556923.0 39835457.0 + Germany Female nan nan nan >>> # using argument 'fill_value', you can choose which value to use to fill missing cells. - >>> read_excel(fname, sheet='missing_values', fill_value=0) - a b\c c0 c1 c2 - 1 b0 0 1 2 - 1 b1 3 4 5 - 2 b0 0 0 0 - 2 b1 9 10 11 - 3 b0 12 13 14 - 3 b1 0 0 0 + >>> read_excel(fname, sheet='pop_missing_values', fill_value=0) + geo gender\\time 2013 2014 2015 + Belgium Male 5472856 5493792 5524068 + Belgium Female 5665118 5687048 5713206 + France Male 0 0 0 + France Female 33827685 34005671 34280951 + Germany Male 39380976 39556923 39835457 + Germany Female 0 0 0 Specify the number of axes of the output array (useful when the name of the last axis is implicit) - >>> # read the array stored in the CSV file as it - >>> read_excel(fname, sheet='missing_axis_name') - a\{1} b0 b1 b2 - a0 0 1 2 - a1 3 4 5 - a2 6 7 8 + The content of the sheet 'missing_axis_name' is: + + geo gender 2013 2014 2015 + Belgium Male 5472856 5493792 5524068 + Belgium Female 5665118 5687048 5713206 + France Male 31772665 31936596 32175328 + France Female 33827685 34005671 34280951 + Germany Male 39380976 39556923 39835457 + Germany Female 41142770 41210540 41362080 + + >>> # read the array stored in the sheet 'pop_missing_axis_name' as is + >>> arr = read_excel(fname, sheet='pop_missing_axis_name') + >>> # we expected a 3 x 2 x 3 array with data of type int + >>> # but we got a 6 x 4 array with data of type object + >>> arr.info + 6 x 4 + geo [6]: 'Belgium' 'Belgium' 'France' 'France' 'Germany' 'Germany' + {1} [4]: 'gender' '2013' '2014' '2015' + dtype: object + memory used: 192 bytes >>> # using argument 'nb_axes', you can force the number of axes of the output array - >>> read_excel(fname, sheet='missing_axis_name', nb_axes=2) - a\{1} b0 b1 b2 - a0 0 1 2 - a1 3 4 5 - a2 6 7 8 + >>> arr = read_excel(fname, sheet='pop_missing_axis_name', nb_axes=3) + >>> # as expected, we have a 3 x 2 x 3 array with data of type int + >>> arr.info + 3 x 2 x 3 + geo [3]: 'Belgium' 'France' 'Germany' + gender [2]: 'Male' 'Female' + {2} [3]: 2013 2014 2015 + dtype: int64 + memory used: 144 bytes Read array saved in "narrow" format (wide=False) - >>> # let's take a look inside the sheet 'narrow_2d'. + >>> # let's take a look inside the sheet 'pop_narrow'. >>> # The data are stored in a 'narrow' format: - a b value - 1 b0 0 - 1 b1 1 - 2 b0 2 - 2 b1 3 - 3 b0 4 - 3 b1 5 + geo time value + Belgium 2013 11137974 + Belgium 2014 11180840 + Belgium 2015 11237274 + France 2013 65600350 + France 2014 65942267 + France 2015 66456279 >>> # to read arrays stored in 'narrow' format, you must pass wide=False to read_excel - >>> read_excel(fname, 'narrow_2d', wide=False) - a\\b b0 b1 - 1 0 1 - 2 2 3 - 3 4 5 + >>> read_excel(fname, 'pop_narrow_format', wide=False) + geo\\time 2013 2014 2015 + Belgium 11137974 11180840 11237274 + France 65600350 65942267 66456279 """ if not np.isnan(na): fill_value = na diff --git a/larray/inout/hdf.py b/larray/inout/hdf.py index 2fc6776f9..b59e64232 100644 --- a/larray/inout/hdf.py +++ b/larray/inout/hdf.py @@ -14,6 +14,7 @@ from larray.inout.session import register_file_handler from larray.inout.common import FileHandler from larray.inout.pandas import df_aslarray +from larray.example import get_example_filepath __all__ = ['read_hdf'] @@ -47,42 +48,19 @@ def read_hdf(filepath_or_buffer, key, fill_value=nan, na=nan, sort_rows=False, s Examples -------- - >>> import os - >>> from larray import EXAMPLE_FILES_DIR - >>> fname = os.path.join(EXAMPLE_FILES_DIR, 'test.h5') + >>> fname = get_example_filepath('examples.h5') Read array by passing its identifier (key) inside the HDF file - >>> read_hdf(fname, '3d') - a b\c c0 c1 c2 - 1 b0 0 1 2 - 1 b1 3 4 5 - 2 b0 6 7 8 - 2 b1 9 10 11 - 3 b0 12 13 14 - 3 b1 15 16 17 - - Missing label combinations - - >>> # by default, cells associated with missing label combinations are filled with NaN. - >>> # In that case, an int array is converted to a float array. - >>> read_hdf(fname, key='missing_values') - a b\c c0 c1 c2 - 1 b0 0.0 1.0 2.0 - 1 b1 3.0 4.0 5.0 - 2 b0 nan nan nan - 2 b1 9.0 10.0 11.0 - 3 b0 12.0 13.0 14.0 - 3 b1 nan nan nan - >>> # using argument 'fill_value', you can choose which value to use to fill missing cells. - >>> read_hdf(fname, key='missing_values', fill_value=0) - a b\c c0 c1 c2 - 1 b0 0 1 2 - 1 b1 3 4 5 - 2 b0 0 0 0 - 2 b1 9 10 11 - 3 b0 12 13 14 - 3 b1 0 0 0 + >>> # The data below is derived from a subset of the demo_pjan table from Eurostat + >>> read_hdf(fname, 'pop') + geo gender\\time 2013 2014 2015 + Belgium Male 5472856 5493792 5524068 + Belgium Female 5665118 5687048 5713206 + France Male 31772665 31936596 32175328 + France Female 33827685 34005671 34280951 + Germany Male 39380976 39556923 39835457 + Germany Female 41142770 41210540 41362080 """ if not np.isnan(na): fill_value = na diff --git a/larray/tests/data/births_and_deaths.xlsx b/larray/tests/data/births_and_deaths.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..5fb2ba5e989fb033872157cd86d1a20d4eface07 GIT binary patch literal 9747 zcmeHN^;aC()6g2I79gwYYV zw{r&BIUA_DJAlA?Y;LwT6ghA(3|UYxkl+7r`!Ak>p15(lE&yuZQS2qUT7?S9IzS#L zu=|meH?JCDfLNS*isl2Fe`6+zVM+mdz{|yaC6c>4Z$}f1{)0X<<`9ckX337-P*mOR zddD;6R$%6tU^FFTVf+QUOf@v_=H$h!Lo?EQB6C3&NqOAE{GO5^DWk8je5=Vf^9&nv ze2HReC@&EBcQ5Qf+ho4EY1(KU#3EgCtgIlQ^yzvsZ4G3=^-0@d6c5{Rl5nDY_|&|!!V8rdX1vdWkgex zk?SfG&@{3>V~SapK^Ij^YKKV?HSUP4XZk4>H>J%_tT4g`WBZ$EQ7me!0Ydx3$ zI z`CqKWKRtR`j6AR#fEsl4=014fYIY?G^OdZt*qatgRqyxG%NX?$xilmz9rUD_s>J@V zuYKCQ?}wIFgd+9_C@(j-%fhg61*scc%Yzbc9p54_(K#hZIhK9x!E%|sn7vGXE#uDM z(iX{7`l%>ec5sbaYWhT?8graYgBS@XpEL+xIN4XbPhM-)___jm_La(y@}R0l-mLxD z@l>C=gn~UZ(GY&Q?^DTm17KtGg$l0$8_J6tVl@>De#;9XJnO ztO{uZ%2eFg*PP?8`)RW-e6;JhPlnUodw7w$O9zez{ldtLRv~%n?;?>YE-<=<%*bn4 zC@5kmL})h~_J8Tb)!xb4*xugyXAb+1W}qR741)6Cy|u=STJ-=>1C9dQ{U_TTV;&WU z1Uwl7+ZvV-0M*#R@-`;LqV9Joafdurd5IIbG~8z#BW8E}=Nn|yZ&w)^YDnRl7gNXT zk@c^D8dnq{^<2$Y=y>68s><2edB*3h6@aEKFR6<8lns-+{o_=1NV!2GS+a?J{bFEZ z6K4MkjAlu#Z#U`T9WSy{EI*_xVY+@J?(s$)ys@`kyg#I}n7-_N$x*NyLLxmAqxzj)iZ+5a427U>7MPy>0 zpW&YFI#-0J(*Zk0pG*y>^rdC0v9~T;YOmo#pq3MgAOcHJq;_1 z!BIbjlX_T_%4I0mXJ&kML@>F5)A)X*P*CY){Pmu|M8nfdTP`)MLKs{1KtO9!G?v14 zhNzCBUdwlhz%QRM{FB_yV$&+)RF27<wkUgcpqTwyYSwfzM}tRIEexjk6zNsIn;RqdVJ1xhII%476olo zhqH5Fao;Zj;-@Mx!o0S24!rhm!v=dI@HgklriC2)jbh68jMK_Dw@3tXW8$QD1$%J1!ps$`U247R9ygKxZJRI-4 z^S%jVkP8*aJwX%}sd(t^l@HZrjCe@m`yBphbFQivPgbM~)qKl{$n+}}GTos-n3P?d z2{(T3+l_A~sINYKRCi9l7-{9Qdbs$2d$($N8#9j#l4&KndqHB;+M;STu9yg4-=2QN z#>e#C%hjzxa92f?v@6S46VPw~=1aL2i|ohv*2Z(jcv+<8J2o+35PmPcbz2H>$vvTQ z=vN~Aq!ouy>Q*xZMP-AwnHKY%}-xM(U-Ux z{y=+5@qy^&@9VI8_IsN=NTH(q3;gHKF z3sS@I)QsY8Ul!T-Qsd#!BK>e0^Rd3iI|UbgQh|m^?@)!EY4G?8Sa~W{0lChk b~ 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 literal 0 HcmV?d00001 diff --git a/larray/tests/data/examples.h5 b/larray/tests/data/examples.h5 new file mode 100644 index 0000000000000000000000000000000000000000..7b8a21dc38a9921f5dc2ed089f5959ce0ee43421 GIT binary patch literal 23112 zcmeGkU2Kz8^n9f)l^-1-5&vRQ(J67+5M)RsEwVY4vALBoG`f}Tb2M~aX*k}PKO`srUe95^P4u$C{A{92DQ5a}xinrs2yLs19&JQ!4aS^gZS^c;z zl`Cw|1N6H|oeO<73Sfo#0;bnex}FD7Jp$42F&ZU;6S2ho7J#Z0t zaAUl)li@-tmvueV(Y6^kXy7Ai+z50vb#y-5wn>kREFRLijVF5i`md;K(ES})q{l4fPbCU;I>-}_$DhYYf&lKprkRHU;m-rS^(^PH zo9IF0{y8oS9~*j~t?9RUW4x=agZRkHADQ97?6YH_wR2P31_x#pwbuTn1SEWNjLYY4 zqk5t9^AB7I7hOl?Sd8+bo(WzESIgxV)<@J@{hx9^u$cC;kGC{+0ZfiV6>MfH4xuFHyyRLck8*uT&5At&fq?E9 z;c;d;OXd-Xa1NCgDtJr?{Ko07Ck!8>KJ?df@s5u8R@KMABecJsTT!*gfe(=enT>tiUJ^)-DyblS&!A`7 zdF1BuaFw=xhs37RDb7!GzTQm_q=pBZbIDAf3lF)u!DL3!r{e?y&F)|_?Y63aBn$lO zez_B(Uv)zNBMEqFXBR|1?}Gd8*#dtZ{lQ7JpLNds{*&V_4+h@*J{(H}=?2^(bt*^~Wbw=WmFYrJC)Fc8kJZnCIr@CFOt_`=AZ`fBy?jeuhs2*+Fzv#B-gaM(}Io5$!%`h zTSj%L5$$Q$uzG;- zAm`h=l*)o7LpO`Hed?uGkGWXJe@pV@Sn-_8`)|GH8qrIM=}-T2eV5O*I{ddr?oW^V zg~X3b<3?tmcybYtKnCg^@4X_P%O%nO?-!@te!D(?s5DMne<#7WQ)WMB7zX^}w4;rL z2(Nr0kwj9)$hnKtkNe5If_;qlO5-|)U&_aqCjUJOZJsDTv++ednv0Du?yjW+9_4fP z*H8QJB9Q9PPo?q2=pDR4pDfDtEfmJ|fdYte%Zb&#Ai6=R2b~&KjJjMRY6dvGsvHzR zFE<}sTOO{Rn@87_hpV*JD`&iRjN$X?q5s8htYJOf%?O(4KK(+V6N<;?{{i^$Z*@ug z9pK(y0N(!@ApRr3!tOA<(-(#{>%y>Xei%;Q6owc72*C?oA^2@11ewo5@a=*SeA*U* zPo4{b-(o5~d<$nWU@>4ZU@>4ZU@=e;2E>jSVf#z$G2>q}-y5v%IOQR}57;;*5(c{k zi@C8I$!~Nqagnj&jfS4+9!&mujH1(x;>yJ-zh=4+PdBlR^-mKXE?=DTB;(!2DK9ra zB4rD|U#U1{FQ0Y`kyV*E<=Lv@N8V1=#3@DJ?`FS@@f(mFc2vv&CG^ydC;nVQNpu2M zUK1YVd=Kz@NWyQ%&puV-iL36W9aM+E*4Rta!=EVe#M1ZL%swwJo_H`ur(0<}anFMU zFZU5D+0U7R0l#=+<7UEu*Ux`B;)&;5X`X$Ia2rpYp6@Fj-arRDemwDf6G2voekzS8 zX4f+z`(#nB9~npB#u%xRgx1%?@Yv2UjGhd`%Ehzc<1fNcyMDHfCr*&qEx5&i#el_t#el`Y zBrzcVr%}GAi9KfGKhq_iSkp`>vhhU3gTxmkH+JJpizhB)y0G!YvFPIR#S@n^-fcW_ zEWBSzeniR^KEG1&!~^V&k@$xH_rOyKW>hZ(}m_CG(XdgD7NO+L*1^%60!P3-^C%(exS{?pcBcChtvl;wP h?!759-~a71{Jjs6@00!g6Nx95{{Cg@yyLx}_#bbny>I{k literal 0 HcmV?d00001 diff --git a/larray/tests/data/examples.xlsx b/larray/tests/data/examples.xlsx index 03811b799f0b452402c23317785a20d862e6d302..5f7243efb4f0cd2c68b7b01d3f8ae020ad956d1e 100644 GIT binary patch delta 7768 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 delta 7043 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 n081AOHFpm>Yc!z diff --git a/larray/tests/data/examples/births.csv b/larray/tests/data/examples/births.csv new file mode 100644 index 000000000..b8136dea4 --- /dev/null +++ b/larray/tests/data/examples/births.csv @@ -0,0 +1,7 @@ +geo,gender\time,2013,2014,2015 +Belgium,Male,64371,64173,62561 +Belgium,Female,61235,60841,59713 +France,Male,415762,418721,409145 +France,Female,396581,400607,390526 +Germany,Male,349820,366835,378478 +Germany,Female,332249,348092,359097 diff --git a/larray/tests/data/examples/deaths.csv b/larray/tests/data/examples/deaths.csv new file mode 100644 index 000000000..9b98fd078 --- /dev/null +++ b/larray/tests/data/examples/deaths.csv @@ -0,0 +1,7 @@ +geo,gender\time,2013,2014,2015 +Belgium,Male,53908,51579,53631 +Belgium,Female,55426,53176,56910 +France,Male,287410,282381,297028 +France,Female,281955,277054,296779 +Germany,Male,429645,422225,449512 +Germany,Female,464180,446131,475688 diff --git a/larray/tests/data/examples/pop.csv b/larray/tests/data/examples/pop.csv new file mode 100644 index 000000000..4d1675f75 --- /dev/null +++ b/larray/tests/data/examples/pop.csv @@ -0,0 +1,7 @@ +geo,gender\time,2013,2014,2015 +Belgium,Male,5472856,5493792,5524068 +Belgium,Female,5665118,5687048,5713206 +France,Male,31772665,31936596,32175328 +France,Female,33827685,34005671,34280951 +Germany,Male,39380976,39556923,39835457 +Germany,Female,41142770,41210540,41362080 diff --git a/larray/tests/data/examples/pop_missing_axis_name.csv b/larray/tests/data/examples/pop_missing_axis_name.csv new file mode 100644 index 000000000..c68fbe20e --- /dev/null +++ b/larray/tests/data/examples/pop_missing_axis_name.csv @@ -0,0 +1,7 @@ +geo,gender,2013,2014,2015 +Belgium,Male,5472856,5493792,5524068 +Belgium,Female,5665118,5687048,5713206 +France,Male,31772665,31936596,32175328 +France,Female,33827685,34005671,34280951 +Germany,Male,39380976,39556923,39835457 +Germany,Female,41142770,41210540,41362080 diff --git a/larray/tests/data/examples/pop_missing_values.csv b/larray/tests/data/examples/pop_missing_values.csv new file mode 100644 index 000000000..6bb7d53bf --- /dev/null +++ b/larray/tests/data/examples/pop_missing_values.csv @@ -0,0 +1,5 @@ +geo,gender\time,2013,2014,2015 +Belgium,Male,5472856,5493792,5524068 +Belgium,Female,5665118,5687048,5713206 +France,Female,33827685,34005671,34280951 +Germany,Male,39380976,39556923,39835457 diff --git a/larray/tests/data/examples/pop_narrow_format.csv b/larray/tests/data/examples/pop_narrow_format.csv new file mode 100644 index 000000000..44a7f925b --- /dev/null +++ b/larray/tests/data/examples/pop_narrow_format.csv @@ -0,0 +1,7 @@ +geo,time,value +Belgium,2013,11137974 +Belgium,2014,11180840 +Belgium,2015,11237274 +France,2013,65600350 +France,2014,65942267 +France,2015,66456279 diff --git a/larray/tests/data/missing_axis_name.csv b/larray/tests/data/missing_axis_name.csv index 28c1d639e..3a69dc182 100644 --- a/larray/tests/data/missing_axis_name.csv +++ b/larray/tests/data/missing_axis_name.csv @@ -1,4 +1,5 @@ -a,b0,b1,b2 -a0,0,1,2 -a1,3,4,5 -a2,6,7,8 +a,b,c0,c1 +a0,b0,0,1 +a0,b1,2,3 +a1,b0,4,5 +a1,b1,6,7 diff --git a/larray/tests/data/pop_only.xlsx b/larray/tests/data/pop_only.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..960b3fd3bcad471c13a3f6e8026d16d6c557115b GIT binary patch literal 10282 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 literal 0 HcmV?d00001 diff --git a/larray/tests/data/population_session.h5 b/larray/tests/data/population_session.h5 new file mode 100644 index 0000000000000000000000000000000000000000..c4200608dbc36df0d63a107007631b9ce28cc0ac GIT binary patch literal 3190144 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 z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBmVn!s18Y*+42dHa?&i(CW<5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7csfvW_*R1ICZKaE;*)f58+2oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ t009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+0D+|m{68iz|1tmo literal 0 HcmV?d00001 diff --git a/larray/tests/data/population_session.xlsx b/larray/tests/data/population_session.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..79dd17636b84926daa57b9339bd89d2c2bfd16dc GIT binary patch literal 12045 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 K6b|jrPyY`edNR}i literal 0 HcmV?d00001 diff --git a/larray/tests/data/population_session/__axes__.csv b/larray/tests/data/population_session/__axes__.csv new file mode 100644 index 000000000..c8e70545e --- /dev/null +++ b/larray/tests/data/population_session/__axes__.csv @@ -0,0 +1,4 @@ +geo,gender,time +Belgium,Male,2013 +France,Female,2014 +Germany,,2015 diff --git a/larray/tests/data/population_session/__groups__.csv b/larray/tests/data/population_session/__groups__.csv new file mode 100644 index 000000000..a25e717f9 --- /dev/null +++ b/larray/tests/data/population_session/__groups__.csv @@ -0,0 +1,3 @@ +even_years@time,odd_years@time +2014,2013 +,2015 diff --git a/larray/tests/data/population_session/births.csv b/larray/tests/data/population_session/births.csv new file mode 100644 index 000000000..b8136dea4 --- /dev/null +++ b/larray/tests/data/population_session/births.csv @@ -0,0 +1,7 @@ +geo,gender\time,2013,2014,2015 +Belgium,Male,64371,64173,62561 +Belgium,Female,61235,60841,59713 +France,Male,415762,418721,409145 +France,Female,396581,400607,390526 +Germany,Male,349820,366835,378478 +Germany,Female,332249,348092,359097 diff --git a/larray/tests/data/population_session/deaths.csv b/larray/tests/data/population_session/deaths.csv new file mode 100644 index 000000000..9b98fd078 --- /dev/null +++ b/larray/tests/data/population_session/deaths.csv @@ -0,0 +1,7 @@ +geo,gender\time,2013,2014,2015 +Belgium,Male,53908,51579,53631 +Belgium,Female,55426,53176,56910 +France,Male,287410,282381,297028 +France,Female,281955,277054,296779 +Germany,Male,429645,422225,449512 +Germany,Female,464180,446131,475688 diff --git a/larray/tests/data/population_session/pop.csv b/larray/tests/data/population_session/pop.csv new file mode 100644 index 000000000..4d1675f75 --- /dev/null +++ b/larray/tests/data/population_session/pop.csv @@ -0,0 +1,7 @@ +geo,gender\time,2013,2014,2015 +Belgium,Male,5472856,5493792,5524068 +Belgium,Female,5665118,5687048,5713206 +France,Male,31772665,31936596,32175328 +France,Female,33827685,34005671,34280951 +Germany,Male,39380976,39556923,39835457 +Germany,Female,41142770,41210540,41362080 diff --git a/larray/tests/data/test.xlsx b/larray/tests/data/test.xlsx index 8a957d23cfacf8efc385f1e11db74f0af824f542..3a620ca0ba15eb27c9c2075738246760379cc2e4 100644 GIT binary patch delta 6161 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 JCB8qs{{t}HF_ZuR delta 4508 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 R{k@X`AVdNHXn}r9{{#Al|6l+B From 6cf6f1a0e91c17f60124665e88daabf704b2d7d1 Mon Sep 17 00:00:00 2001 From: Alix Damman Date: Tue, 24 Jul 2018 12:25:29 +0200 Subject: [PATCH 2/3] added range argument to read_excel --- larray/core/array.py | 2 +- larray/inout/excel.py | 26 +++++++++++++++++++++----- larray/tests/data/examples.xlsx | Bin 13313 -> 14728 bytes larray/tests/data/test.xlsx | Bin 15265 -> 16173 bytes larray/tests/data/test_narrow.xlsx | Bin 12427 -> 13676 bytes larray/tests/test_array.py | 8 ++++++++ 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/larray/core/array.py b/larray/core/array.py index d6a7ac69b..aff5e735a 100644 --- a/larray/core/array.py +++ b/larray/core/array.py @@ -6285,7 +6285,7 @@ def to_excel(self, filepath=None, sheet=None, position='A1', overwrite_file=Fals existing file, "Sheet1" otherwise. sheet can also refer to the position of the sheet (e.g. 0 for the first sheet, -1 for the last one). position : str or tuple of integers, optional - Integer position (row, column) must be 1-based. Defaults to 'A1'. + Integer position (row, column) must be 1-based. Used only if engine is 'xlwings'. Defaults to 'A1'. overwrite_file : bool, optional Whether or not to overwrite the existing file (or just modify the specified sheet). Defaults to False. clear_sheet : bool, optional diff --git a/larray/inout/excel.py b/larray/inout/excel.py index 1801da04f..9d3651986 100644 --- a/larray/inout/excel.py +++ b/larray/inout/excel.py @@ -27,10 +27,11 @@ __all__ = ['read_excel'] +# TODO: remove '# doctest: +SKIP' next to arr.info when Python 2.7 will be dropped @deprecate_kwarg('nb_index', 'nb_axes', arg_converter=lambda x: x + 1) @deprecate_kwarg('sheetname', 'sheet') def read_excel(filepath, sheet=0, nb_axes=None, index_col=None, fill_value=nan, na=nan, - sort_rows=False, sort_columns=False, wide=True, engine=None, **kwargs): + sort_rows=False, sort_columns=False, wide=True, engine=None, range=slice(None), **kwargs): """ Reads excel file from sheet name and returns an LArray with the contents @@ -63,6 +64,9 @@ def read_excel(filepath, sheet=0, nb_axes=None, index_col=None, fill_value=nan, engine : {'xlrd', 'xlwings'}, optional Engine to use to read the Excel file. If None (default), it will use 'xlwings' by default if the module is installed and relies on Pandas default reader otherwise. + range : str, optional + Range to load the array from (only supported for the 'xlwings' engine). Defaults to slice(None) which loads + the whole sheet, ignoring blank cells in the bottom right corner. **kwargs Returns @@ -144,7 +148,7 @@ def read_excel(filepath, sheet=0, nb_axes=None, index_col=None, fill_value=nan, >>> arr = read_excel(fname, sheet='pop_missing_axis_name') >>> # we expected a 3 x 2 x 3 array with data of type int >>> # but we got a 6 x 4 array with data of type object - >>> arr.info + >>> arr.info # doctest: +SKIP 6 x 4 geo [6]: 'Belgium' 'Belgium' 'France' 'France' 'Germany' 'Germany' {1} [4]: 'gender' '2013' '2014' '2015' @@ -153,7 +157,7 @@ def read_excel(filepath, sheet=0, nb_axes=None, index_col=None, fill_value=nan, >>> # using argument 'nb_axes', you can force the number of axes of the output array >>> arr = read_excel(fname, sheet='pop_missing_axis_name', nb_axes=3) >>> # as expected, we have a 3 x 2 x 3 array with data of type int - >>> arr.info + >>> arr.info # doctest: +SKIP 3 x 2 x 3 geo [3]: 'Belgium' 'France' 'Germany' gender [2]: 'Male' 'Female' @@ -179,6 +183,17 @@ def read_excel(filepath, sheet=0, nb_axes=None, index_col=None, fill_value=nan, geo\\time 2013 2014 2015 Belgium 11137974 11180840 11237274 France 65600350 65942267 66456279 + + Extract array from a given range (xlwings only) + + >>> read_excel(fname, 'pop_births_deaths', range='A9:E15') # doctest: +SKIP + geo gender\\time 2013 2014 2015 + Belgium Male 64371 64173 62561 + Belgium Female 61235 60841 59713 + France Male 415762 418721 409145 + France Female 396581 400607 390526 + Germany Male 349820 366835 378478 + Germany Female 332249 348092 359097 """ if not np.isnan(na): fill_value = na @@ -198,9 +213,10 @@ def read_excel(filepath, sheet=0, nb_axes=None, index_col=None, fill_value=nan, .format(list(kwargs.keys())[0])) from larray.inout.xw_excel import open_excel with open_excel(filepath) as wb: - return wb[sheet].load(index_col=index_col, fill_value=fill_value, sort_rows=sort_rows, - sort_columns=sort_columns, wide=wide) + return wb[sheet][range].load(index_col=index_col, fill_value=fill_value, sort_rows=sort_rows, + sort_columns=sort_columns, wide=wide) else: + # TODO: add support for range argument (using usecols, skiprows and nrows arguments of pandas.read_excel) df = pd.read_excel(filepath, sheet, index_col=index_col, engine=engine, **kwargs) return df_aslarray(df, sort_rows=sort_rows, sort_columns=sort_columns, raw=index_col is None, fill_value=fill_value, wide=wide) diff --git a/larray/tests/data/examples.xlsx b/larray/tests/data/examples.xlsx index 5f7243efb4f0cd2c68b7b01d3f8ae020ad956d1e..e1f1c265df7e0ce71e418c6ed05782d50bc399fe 100644 GIT binary patch delta 8680 zcmZu%Wl$Vjx5Zs11Pksi14HoO?iyTzh5*4`1}C@-?j9Ht2<|SyArRbkkl-Hdk=**; zt?$+AU)|N~oU^;u>D7C$-GT2vxe%!-A|m0#p}?WR!NF0(mA=d{_JW6l>#QQ8LjW78 zb_4Olj+O6m6&*(3ibE77xmCgq$en-YF=@9MI7se7{6y$D5SPz72_o_HpXIL#Uxg*>rWzB zSwtP?A+^xj03CC$ab^eee3Z~_0B#_v_wbbL0ckXAKx@}e>ASU#@ zvVaDwo$WT&sYcb@YS2FcMF9e1LA0s%k|vj%5Cq_1!kHw?>p~Qm+O}6VDZx0jxQf>! zRF_Z4>$C#i?CXsm(Z4Gz@_#hw{^5ZBeOc7WGb66E=lp_|)B?v)?V=p-WXYW66NNKJ z2;%J#IsM=~y#I&e%{&1i_kJ4s*x=2UZ!t;Dg7tJrW2|$b3R8|N0vh_+OYWuTYC(1< zJJjIt)n^i(o)F>wAq9esjaJi&NRNmyc%>CV@k|RkO3eWZD?IpPFc1)Wo$QUe<;N8t zQmd`Rt6gljcmYRUh5?QT$9~ZwJTfh z@R*%D@lD`e>g6@G?>bhA5#*;tE}9LBdF!qFS4Vt7?T@~t3ih&%d0%q&c84ZkYkX!a zyzEhWa?d3WtU!-Az)JnX0D6c(dpN(JqMY^Vr)-FR^T9Na5Y!f5jjpU&gk1Iw+-Qbf zwoYf=+KrcT--VCO$o~?Vx|&6KvD&w$yLQG0lNua{odV;b=TcmZ{8`P{EgwPONrY_Q zYx-4T)x4tnzREs2NgF$DZZdJ`^H^zxwvFex8Ys4bzR^!C$9?VOe4SDcIM<`>c(D(S zEXhJo8D<6AU*(zH#<9K3C!*#Dx5jqeu{AL;3PGLu3pO56)o%;gpDeKSI_$9F>jW!8 z0Ij(C=Q4KnA{}Ek^6l3@ryZy6Ls^r{G-egpwpa=iEkJ$(4Ti&Vv5OijhzY3YYOmTw>)z0#_kL24_l%*}tJX)rjq`tCfe9{xhlAjI>V(c5 z6a@mi5&Q!yfg3!%22N3y`E{*Xd&73{vAJRLL7QgT?;>Wc zefuh-Ho-u`&n#FBz4_l4z$X1jROgPS+h4axd{=u-s6gON@-6hac=F*J6uiJJUi+6hEol%>)YgpGkN$YP3WignhK+8_6{J$e{ zd(f-9Q>++?$7fE*)z15XSr1w`Xw0l@E{_j;Jux-yXSC)P6TXk&524xrq77sIbI*_$X>BQ<()9A?Q2rB1?`Pc5#tYJ||m&NezQztuwXEa1Hn>yv463wt3U z$l^iZvwm--wz6Ig4u$>NBJcJ2d9m7DAGTq3r~CxXW|n}wB49Lf!wOV&N|#Vo*CaX| z{3(%*Brw1!F;@95%(*1qbW8t7Z$@)iowINcb_p+FwJN5VbdlAeI#LHy^@T}W!}_tO zTXO{-4sMf#o+-z%fC?pby$79%DSYxmRX0)=%#_IZ+c&ZRxm^Y(E<>KW=)P_bY)%z2 zhoFt^Aw?C_pzwazJ1>#*o%?SNTo5U$1ZGE3NaJ2!`xJa-#4RuqPTuuIqOGV4U_-%QQys~1x0_{>Sa+VDG}bU$+3{uNe4|*HUj8*AW~wvzFblfh z?7GY?La(RAac1ib?>rF223@mtavio!kh=5MbayX@;D+<4xO?4a z(5FJVH0WTZ2v5VB-s~}TZ0+9-2?;nWXT7qlb%3W?IaB*Jy!#{7p0^L4gHgM;SWaL7 zVP8!g!XwlD-46HVaUrXvjUjl;(!Z3@bl%>VBD~UYTgJ)ShV0463e6>Ii+visHN8L- zhPR&0?hV>`D&qR_YO0}Jx4q=9?)-Ra=A1(NhR2#`%jN2Z3KqEb=!+=|U4AX2BM&^e)%MFTXV2Sr?3@U@y~jG|sGE z73ar6({B*?iCsSKGe%@?aH``V_hr zvUFdn*#u&Xr;uc`OQ#dL{OE*xB$~M)%64?$v0ZbunLmE=U|shll8`8Y#RQMSV&WKD z{jxqDZ_w|9JiON319L#%4B$7`=KwzH8~9|JPU_ltFA5^O>~!#i7>Q@(>KYZMnF89k zMojqS%5ae$J%BRW5UMPl2nvcb2fnnBBqCxih@(RU&G{KLMXWx33KstU>g zeNLovrdmT7}!u4&h`oY@)VwT0FzjxoqYqNd3QEh zmBA*>*#1xqk-zyVhK#GbSS;|^v@kX%L2ObTiTxb^T|Hg~oq18Ewk|4)V^WYzf-k*i zz|vAzzaUatPn$tpSs%S!dZn-myMzn_bNW9sfrlhm<&}`}J*ehNB~L;FR3cFGQw9U% zqvH1>9JK_!L{whJkWxPH53H>rTiBW&)THi+~HChxa&#JwK=x zF3u|%e#A!0{D_@X3G{Dl1&4)AU&$|OG6uk zfEYnI5;aPX(g`5_n?*JSMw@30?z*f#iV!JZ#D?w&Rxo(4wds+o&6%CW{C!kFW}@*z_q zm|WK?pFd5jc#*J{S!!T+&D*3`#!(99_iT>s{l52@H{Ay_RjD2z>mD?Vjsx(RQ=7S~qHgSAKyfz#?GhGsU6l<- zBT6$OgUmG93H3*Y569IFZ;t%e-iS}nAptYG_b z0)ssPG1O>F|;E1hCYV`;f6eewUH%)e-vnomjMwkf1_4W!ginumW2i z+l>Wr&Km^WF240Yh?(w|kco*jF4+haCLTcy6sI?XhMZFCO-%OV4WI_{Wct-l$A;0aTMJ)!pu|4k!0%sO@$G2HAzk9k2 zt1B_RNeoiwpnq<&EDF|ePib3~nT|CWTV+`%sIMwlJ793vY}mwo_e)nIF8%l5_^NKR zDATSV5mh;hsypQi2v;r9Ty)i zqlaZ~2(^vVDSp`4bp%iyQ&Is*>U|_`{F&@E_aNk6a^9p%Cp)yGZ&hK-vpmfN6;(!z zwTLk-+VKbBBsZ2ZFhdCNnOW~FvY_KM;R;(O+#rO8F%tSd^0sZ0D_Oo@1d*s6}ZFUTKh-bKH*SyrHP{c-sL zd+ot`jrxYe*JKKG%y&W|9>|+k@Ro*R-)OM8&=er81Bt$=AlkK`8d- zw6S0-gE4o0vRYf%#E4FvPP)t&syAfLG0DL>fp$J;c;=0j=&=1Wwb9F?ZSzOUKg6PQ z^=O{`d@gqVAuX@S!Q!?(K&;T?;78QLyZzd5d#vxFI18E%i}pm2_YqwwAVrSDj!IP0 zXrAxad-C3i^G0TB)+4`O)_v3C%ZqRR=|iG_<0&fK9$@D5vn3wQppm9$tbziorgTAZ zshf$r?ZfEuryr++%5rsQ$oATEa~CCK0+9cZ5BFyaq!yJ2#5Q# zV$&E$5?IsiA-C;s5sIE`bI^Y5@18 zWJkt&7&1qX+I83zP?&6fqz_s>`EgQxa#PBZ=Xo)HxF_(YflrCj_7L!$=12JmLaY3MxBjNqur{EO?7{K#jsC-lTK&D_Lf^@tDsaLkDl8& z(7#Ppak*;y^o^^4&Um;p{%|W&q;f{_w1MOY1218(1pDDK(AOQ?yZo1e!bj;953AC_;5V`n=K83fB{aE^5Swx7F^eO{!TX#q zA{?&;iWbu!X1@=A3S}axa(odh95z$f$88uZxBJ^Q6sTXB`tj!#a zucZG;yZ>&{-KFHI@`EIJguhp|Vfy$Ef3qf*VMse18OsZWFdG(RJywJ-X`i7C=V;H4X8jBCM5Ibb12(+halyN4HIr^=X;Bhdl%gi8TH zQ6PJk7%ufbXmd=8Tn+($66Bc>^z;H#ZOH&QW-HJ;f`30Fu^R*iSQ!W76#c}FsN&x5 z>@23RJT^hPdWQKRp9^=sP<9C}u zj}0N6mfDH842f1dL3GnNmOK^P&qCgd_1lv6S;&{CD2y4AMs#?>7PI8^+;A^{9h<1_ zJUzQse;h3cGYwA7I^#G35)s^s83;8!C7ADT`IcRnUuTfO15sv`7c{6?+ba7P0k{vuD1NDwQ`6(G&+B9U@ zmR)EfItzK_9Bt_~rl1`q;Eji}Y)L6vBwI{5qJnl1AYs4r-r640Sg|go&IJZ($CP$y zFd1=SHh3RoJR4&k;m}F?9NFc_L6S?1h<0g7g3v9q;+uB7xyi;uwB)73VdV^6bgAlzNm6lB2u_2ZfIhy9YaGNfWQpx?2iAZud;Y7# ze10dK3Aq8m%yg@Evq^nF-|ZeDuJx+a$IX2?2F7=|RbYBI>96yB94ztCgHTE$;A2b9 zmMZHz{%FzxC3$|aFKL!~(CHt6GvZ29-`rm~PL|y553+54J$}OUOb-Uy9P0*98bPw-K%hs~I90gqV}> zVe=H_z`|+k@*MKvVCooxGYKnVJ^WN5GL_@f7las&6vdzV1t&?Fuq6}?6x3aZv0u1#Jgn5lRAjhs)4m^)(JO~eE#tJCBLE2JGrNBgD$GwQ-zC0k zkDiBc0SGYy)e`Xj3O5==oQ4wo)+lO04BwCQLNuh+cE<-#ov0(_Dql@u@tP}^WX7VK zZl9z0sf{lpLh!945q>mmEZt>ismbEzBIBqvxL(?(G3X+n+0n9oOHEZ}3$Sm$=$&%VC(C5=s1pgBvumeb5bzGO#eGm3;D^ zPdvfk>)Xo$S0X@&R6LaKoHQ)ZFQn(oto#=k!^&qtR#Loj4=B<@AMapP3VRG?zGAZ9 zJgjZ5NI;DZVx-YNUDTf1A6%I^qFc8Vc&sy`^{Y+)25>X!p85MJGkcP*z65wsjXNS< zcst{dFBKRmRATUi`@3^cy^>yU zWhBP+iFTfhA~c)n0i7!K(Rt^nbay;@HY!l94<%b)dT*(om{LEO3V*qSc@)I;^*L02 z_+HG#NpM0?FRpFlsCj4Du<3E|^+Ny9V;k4yz5p^>AF=|Ys#XAdee=#dpb5J|$Pigy zE^*O@o=#*GqN=}ZBx-_^P~sj{kl}7Z-4Xi4-UgKS5Mu`;%0;QpUEo1x=dfm3(Yh~4 z(mdc=PzcF8g{a{Avh6-yI?bys4Kl?3QXEm|qph@6Q-bKrp-G@%6>#T4j1-Ja@iKON`+fBbC(@BcSL#Gi^5&Cif zupwavL#d@gSMMEP)5h3%LzvfCvz9e`f#;(6+^qN%1lR)`2NP}?O7-eBz3+;5?jHxO z4+Z(gAv3kxu0Mm~>z>O@S`EQq+m#?B+A%VKjN9u2;*y}e6D(fBjHadNDMR}UK1)9N zx1K_671kVb)uU=M4+=ShuwS~zS4e;qqgQlSNRza%a}tql>cc% zK+v$UF&^C)<|>|Z857h$t{X&*9u;izN3;7@84rc1R$fMT-M`HLfCs&NH_2n~zsZy zLG;c;UUM~2B1e0wnhP~tZfUbCs$gU;!}>1c^#|aKF9jGk+ZA@(yKk+p=cctiQhs;n9?%oNPX0X>XQJD*%se0hKkzZR@ZKIgYJqZ!kSQRd+FTOJ zP_-tDm@*XR_hXXd#{J@GfNrdLL6+&cMlmAC!#6+`8&+93)2OMeOJ0ua9B{7)y`*cL zl@LU?z+DbPV_>0%cQ1N)FGeJ5cc2@A7f^X+3-+;;A*quP7xoW9<0q zKK7;QDrRlq0c4j(O4tO`P%x5Tj9MT{hh1#p*aU$oM}MN=TkFpYw1oW733!0X_yiN) zMyM_+dbm`>a2C+k6mdt&3gZ(OqLQE(7XaO(YJ@n%2L07{Gti@tQ^wFe;MiFDYnEEej7kc;c0PMtxNC zuR(f-vG=~Tjtws`=>y#Ft|#5gjdr5qmRxT5VPR(NI&K%(GlMdzpBRv&^So$_R#JA| z+M+@D0^_x&ZiV?$sE+TAjZ6rZwpy!B>_`!36dl#6Y*Jk`3$m{nzR#VKGwmddb&tZ{%(cVhr{5xE1)nGZ#~o3lN9Cksi`Hl-edn7|(PHyakQaoMUPgl3D84v@ zjdznHH08m-{3))ZB=#Gs*rPT`VU_)2QT8R5gn#6!jw+|$wC?^#9Wpg-v>X@A5x)B7 z%54#SU~wA9iN(s{H>%d1ppSy3(@SYNlK^@x#!?!(g((NB>|F?>&7Ln)t)cl0>}t+2l5Om4k~i=7%C z{KIw;20=v2k#*0%~m^SWgE_w9P}!Id$e zi6e74gf(M`3G*zh{pk& z*}2^@X{v&~zoUJU6hb=Wv+7^9FWvV{zo*GLL%GV}B_wGNp8C`e9TpNa01EkLme%N@ zbv-O#8T|gR@<4F5Wjyj>odox))1+J1Z|W+Y;(lRSym!+(i*Fkm7C`Utvr)uca^>6# zX)2WM!#4xAxSUyuhLO0QD0YrY>-q5LYu8E0-kv=n_&@5d2#>%H_y6?%Aq+qb_-cq3 zkb>gh#xooo$@92>k69ErILIjq31l1yApN)J3I~V#FXs8|HRLythV0+Q4ICWtf8A%p zhQt#RK$N)%$o~49&)=D@=U);28zh7G7X*0*{dI)?UywP$U(ozB=zraX=Pir>U(11s zA!^(V$bZXo2sIZ0Lg=gKQ6vyLZU96QM1oL91~CCq{!^pCg9s5qDE~|f&j+F6#z+2} z8~z<7NAq{BU2X!hf4`3ZH)>D%zaTMs1O5x3$qff*;b5-j?BM9a`OeYtPc@~ci2O{;A0z;d?zvH> J^7_x!{{Z8zF>e3> delta 7370 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 diff --git a/larray/tests/data/test.xlsx b/larray/tests/data/test.xlsx index 3a620ca0ba15eb27c9c2075738246760379cc2e4..61b849a2610824ef287ad98353cc66438aee2f76 100644 GIT binary patch delta 6210 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 F_#ae1Qknn& delta 5342 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 delta 4331 zcmZv92Qb`S)c#s^mDRiGL|bi1^cKAYtCy(JtsX>MB{oXb)jJ`gMDK(k1R=^Q5vvms z(V`Q4-u!3&@4Vl9_s-n8hS96On}%Mm}8U=@DbJ`ss+f5#f+K(x;+ikKUYXx%J#A>1brII_QKpysujrqm}@_= zO!72tDcG61elS2}==&A=a>)#xt?)|ff)5OWY4JBlhAdvD)iHw$`?!^I_oh?G&Wmgq zOW?Lm&r1E|f__bG^x({|jji809+03?1gRRku(_}aWlh6_$nFB#!)e^X2N}?4|k;r-ce^9t9F63k$Fd(#KWuSGtHCu zf$n)m>M@NfMG09DkXFCEsB7iviuwdW72xCeXCG;^}eVhsxvHmR& z7Y&|NLj6v!Pq|Mw`eRwGLoFgczDCheqapLA7CGdUGg)ESSEzX^Pbw>YTf19X$-6R4 zy?@WN!;WpznRJu?c{g)I-KlsoMdNN(Y;+g6(Tw6d`hti7kfRXRnyla62zO@Tb;?G?+cPg`er`t=>tkRa8NzXM^`F6pbhYuLp@ zz(03HXf9Ht-V6i)HgQpkAR2_>XO9J8vM$;m@&sM{L8iYS)mbXHMB?$pv3jM(N7z9o zY24xrHTYWeHmm#G#>hA!MH+FeAMriM>No$MGh z+!+s8eO6!-Sxgn&g^n!p+GnucS=gQc4w)2pw20A$gcQFs=u*kpa0M}@T1i3dF*7@v zUXr{M2{Z8d6tGxq0HoY_K~`~QGc<;D@MCN2DDBHrpxU%a?iMsHlWr0XCba~)nwa== zeU4)ikD=(&dc;5HkcyP*t zk{d3=A3T>s467lcxgH3*^0WK*Aa5&c^&T`y)=M~}zK>4GaC2ZrWJA4+e)3h&f3omg z>tObFbb@=PrK3rvF#`HCU}o%>{jn?$QcTOVJl;~+{IDd9D*2OfWR2@wzZI?A{?6j^ zwPRe#=emO|{u?ElUi0N}-fHv1tkKsq`GdzMAC6`cQkPvEkK>{cFt1-UQL#HlU_4`4 zw$*nAHwv0VIieD#FDd8HXFEBgZ*;0K(2lwB8F{EL*k9d!q|MoG~}INT%3znwOj;XRhNo=bU!b6Vw|{uT<@HHEYUG?W@_<=5btq zM_i^vg>g>Fse!H3GPK?DRF;_|<|Kj&vTc5Dne^_MG*7^a*MkrHtwP+Mlxsb}E+|Il zmg#tJwBNi{EX6-k$@N!3i3n8EwqDMtcibi3IE&Y|4DIFlgRabhSVz+ZoVl(=E&Nc9F8kf4AP;+7f01zTn4GleN zkr*GbkeDk>hTP{rxkq*e$|Cd8>vL@s`)OrZ8PpLFqoD^dSkUk{I$4w+WE3ust4>po zDExCY4_&uCIY{Lmj231_yTeQB;S8Ka+j3vPZbw^5yUrdAmMiW_lsPHQ7n*GsD~LaJ z9WehjHKi9=V0!CZHMZYn4u=Qou+dLq_vsNl7fddsD>E^-YUSd0m7I z@$`4%%w9)YGBF4d%Yj4gw+atj8<&cK={LnF?7dp3e1k8D+-z6t{9J1h$s|4yRfJ%@ zwvEGP7b!?bOQ>8lw+lVWKepjtzbUy?J0_px4E6nPA$Hl6J+8Vpd#ai{eC$FM*z35e z^Jb5!xaUpJw@=Bo?R-%w&TzRBhj1qJZxa=)>$u@`w;qy+fMpFOB}G;OQ4+I4X-(bV zy-sZoQCvdI$r%zBSbP>#;j_Rp^dDZJGL$ngf;3wZw-sxz%e(_t^ ztqvA_ai3d(YY|Q0*aIVbbDe1>mLb|vu=ilX3*~{Y(RlW*f(!QZ?qckj{egJ+Gl_(X z=!F?_D=p9~(_rHAFy)H2CqpI?zg6aL@&EoLL1b+}+^k;v-7b_T8#4kb7zlUI%Y5RQ zD@=vlzrQQbx<}~+ynrdqh$XbdYoMEUm)N)!aYx-#n{rN<173mXh^Y--4g1(?S+1B0 z=c-w()`!|C1|Q|PYm2q$L~+W=B8OeR<8YdZuC*B*+dVY9~m*YI^mM5_~7Cv50_cE53+PFFi zk0f&9zVuIPyVR$!7byfzAI8URNA_Lom(>IZe{H5FhA@<~dK0`B4*sm7!*Ob2tqXEE zC+qF9PKgR#Pw!pEkwaezUxdO-C64dj&(L6zyM|-vrIwqEEuzP8YHL{wPm+dXPLH!L zH7A4^)Lu4QN|76;=WZMqyJ=UY6^O|~wAQ@0F3E-lh4^Q1e0oOv`W@8zm1B!^;r z@Y>#O_R=Mt5^|uLST!=?BF0#{NetqdPdl-njA`?M#{|jfU9tnMZJ8K3C1PWQ`1(H_ z>}wXNQ^7}_5XmCvg_cH^QKd$%m2&~tp;rCN+1{L3-OBT%L03wBXvrv&^}bPMm+C}M z>Aujn8gxN2-dYp)zSOlk9y=<8krU_H)mu!AY+shOo(?o79SFyGXMLJ!oSUVd_`}{} zWbYw+S~^H`^7U~-v8W@K)i6Qq{1)^H9d@zGVs=|1zc@S58y3p<0 z2rfmwr7N*`M(|u(RfHn@Rg~mA8`eqCn?XJMvH+OJRDs6Zu^Sr?emeCunbZ4(&#SWx_p18hWc*XD@L=Ezt$`3H`Hy*YQ8Wpkz*7q@m>z_RIvjgiif zEQlhX3-M+!Cp*n`MzF>^R`px1+x-9gy7uSNw5qrUJmd-!vQ(R0Z5B${@hcTc9|m`Nmyvb82T>)*d&Y+eD?gYn%J|l2EybkBWP;}&??@~3 zV|sA8DL_Q2-RT%ZBZ#&oc20(VK$pB~9x%A-L7G7EA+6E#eqXT}Hb@Wr_?LpgY^ppa zx}V{z68D%R@Un4a7B$PwBU(hJiwZ0%Vx+3s<^9g&uSWK2HjSU*kkmTTntPP7E4eZt zWiCG(1jXrYFglhCuc|6X{^22|xBXr1nWW{YVuNq|rC2MS=Q3m1)ehCJCKwZ=EA~1@haw+R7yeh^t(FLrsACwCQ zfo>atgKY0ZsWe$lKi?wH(#VRPYZ;P#}Dy_K!=_tyZ9ILvxhw$wRC zMAUMZB)Ry2!?ulHzTCTXVLhe!&Db`}u%kRZt-NvLH`#TW5$b5?g*VSzK^#$_=eCee z`1}(I5i6R@Ac}*>B>_Jt%}&0^^~txbwM{VC8Ki`&d=r;6Dyoz;61Rm!YZ5jeH^4t1 z{kiwIJnhiKi5g9K$AP$JjyV3Rjr;ie@IF8Ax*xHsV(sxzlOQE zzc`0*c(sxfafSO|olW^yJ^3D$gz-I>*g}SMn;0FF%nqB=^5_u6DIA%rzlTih1ksP{ zZOEV_jhd0bdmoCQKMzg!ee@i@IzNk7ow-t?FqP=8^lvQwMTqIkn*qI_Mp4}c$(nB7bWQMb zl&9Xd=5R}-Y$*P4vw;m2{ zve6XDU!Uw+v>nK^j(+CJ8S}Eg{X_`{o~}_&!XuA1Z4&Z^EpaEspHQUJYdDDvuf3{9 zgYuUqbAP=Kz5W%(*vRtM9(haFn346^MOF19Wzp#2z#I?aFZTy|4BUVD`c4`JH`WO9 zVj)h!$i0L-KL}F6SfmsZWLgibj{ zGmmlZ;{O_w(MX(kTISsuDGT2h>TV>~Xos2Y7ul#O_;d7xk?;J#Ru&C6Yw$9y%i$4_ zRD1I1wO7WJpaUNZqslg20LCPJ(rf3W8-b-3lCm|&1jea(@td6(Ssh^6a%_g4+DRlW zZ2goRUUp6%DQ>R+6DG%SPxdmdp)(4P|6GEXViE6HjzMa~^59V4y{Vy2bFcwMqj*57 zC=>n@*<#FHLIr_XOl;UAze5t>y$$k&#r?ddRVDg~6vigAERX9+)b+d1&*Q`ah~kxs zB3j+mHh-pE&)+`{lOtVhohG76KT>(S4FPW|XZfb+*Q!JgV~e+Ty}xf}!HX>OGKI!J zCn#!jTrKi-zSL-pT3sqm!a-f33U0RTHgk(GBI44@@(IBM<#i{maVw$T2Kw%<)WzDJ z%rw#jYizp$-@ZIG;%|-m{r>x{@`JO{f~y|Rt+~cOqPjxPrJ$Qbw+-t%QGI)h4baoV z0zv@)m&`_`is;K$-ces`9|w0|A;Evf|Be1v0|NjIcOL(3<9Gl7>JX0sB_&G6_^%cO z07(DE+|5`}4x;Q#|B`F~fbKu%Sj4Dt3TjlFs1VSC^zZaWl$!A`tGJ8I`_9>a@sub* zs0t95<}Wl3O3nDUOx?K*zDq}{|F||obwCxdv`~Mb3cwN8zXk?k)TkOU24EW}YDA2M z>2G1UvvazWvE=`;eR&^+E6$<(|MUDV7P9{UaQFa#r=IqDKAv8_LbhIB|BxR&E!?|H R0RX^vJ?qY5QQ)7|{{VRz!yo_v diff --git a/larray/tests/test_array.py b/larray/tests/test_array.py index 9c374b3cb..17b60952f 100644 --- a/larray/tests/test_array.py +++ b/larray/tests/test_array.py @@ -2888,6 +2888,10 @@ def test_read_excel_xlwings(): expected[isnan(expected)] = 42 assert_array_equal(arr, expected) + # range + arr = read_excel(inputpath('test.xlsx'), 'position', range='D3:H9') + assert_array_equal(arr, io_3d) + ################# # narrow format # ################# @@ -2910,6 +2914,10 @@ def test_read_excel_xlwings(): arr = read_excel(inputpath('test_narrow.xlsx'), 'unsorted', wide=False) assert_array_equal(arr, io_unsorted) + # range + arr = read_excel(inputpath('test_narrow.xlsx'), 'position', range='D3:G21', wide=False) + assert_array_equal(arr, io_3d) + ############################## # invalid keyword argument # ############################## From ff2f2a7295b362e5426fa6cd1a8c47b3e41ff057 Mon Sep 17 00:00:00 2001 From: Alix Damman Date: Mon, 11 Jun 2018 16:03:32 +0200 Subject: [PATCH 3/3] fix #645 : entirely rewritten the tutorial_IO notebook --- doc/source/changes/version_0_29.rst.inc | 3 + doc/source/tutorial/tutorial_IO.ipyml | 752 +++++++++++++++---- doc/source/tutorial/tutorial_IO.ipynb | 935 ++++++++++++++++++++---- 3 files changed, 1430 insertions(+), 260 deletions(-) diff --git a/doc/source/changes/version_0_29.rst.inc b/doc/source/changes/version_0_29.rst.inc index 74e689432..1fc8c6952 100644 --- a/doc/source/changes/version_0_29.rst.inc +++ b/doc/source/changes/version_0_29.rst.inc @@ -348,6 +348,9 @@ Miscellaneous improvements * made the `from_series` function support series with multiindex (closes :issue:`465`) +* completely rewritten the 'Load And Dump Arrays, Sessions, Axes And Groups' section of the tutorial + (closes :issue:`645`) + Fixes ----- diff --git a/doc/source/tutorial/tutorial_IO.ipyml b/doc/source/tutorial/tutorial_IO.ipyml index a04c7e539..0edaea2f4 100644 --- a/doc/source/tutorial/tutorial_IO.ipyml +++ b/doc/source/tutorial/tutorial_IO.ipyml @@ -1,251 +1,726 @@ cells: - markdown: | - # Load/Dump Arrays And Sessions From/To Files + # Load And Dump Arrays, Sessions, Axes And Groups - markdown: | - Import the LArray library: + LArray provides methods and functions to load and dump LArray, Session, Axis Group objects to several formats such as Excel, CSV and HDF5. The HDF5 file format is designed to store and organize large amounts of data. It allows to read and write data much faster than when working with CSV and Excel files. - code: | + # first of all, import the LArray library from larray import * + id: 0 + metadata: + nbsphinx: hidden + +- markdown: | + ## Loading and Dumping Arrays + + +- markdown: | + ### Loading Arrays - Basic Usage (CSV, Excel, HDF5) + + To read an array from a CSV file, you must use the ``read_csv`` function: + + +- code: | + csv_dir = get_example_filepath('examples') + + # read the array pop from the file 'pop.csv'. + # The data of the array below is derived from a subset of the demo_pjan table from Eurostat + pop = read_csv(csv_dir + '/pop.csv') + pop + id: 1 - markdown: | - ## Load from CVS, Excel or HDF5 files + To read an array from a sheet of an Excel file, you can use the ``read_excel`` function: + + +- code: | + filepath_excel = get_example_filepath('examples.xlsx') - Arrays can be loaded from CSV files + # read the array from the sheet 'pop' of the Excel file 'examples.xlsx' + pop = read_excel(filepath_excel, 'pop') + pop + + id: 2 + +- markdown: | + The ``open_excel`` function in combination with the ``load`` method allows you to load several arrays from the same Workbook without opening and closing it several times: + + +- code: | + # open the Excel file 'population.xlsx' and let it opened as long as you keep the indent. + # The Python keyword ``with`` ensures that the Excel file is properly closed even if an error occurs + with open_excel(filepath_excel) as wb: + # load the array 'pop' from the sheet 'pop' + pop = wb['pop'].load() + # load the array 'births' from the sheet 'births' + # The data of the array below is derived from a subset of the demo_fasec table from Eurostat + births = wb['births'].load() + # load the array 'deaths' from the sheet 'deaths' + # The data of the array below is derived from a subset of the demo_magec table from Eurostat + deaths = wb['deaths'].load() - ```python - # read_tsv is a shortcut when data are separated by tabs instead of commas (default separator of read_csv) - # read_eurostat is a shortcut to read EUROSTAT TSV files - household = read_csv('hh.csv') - ``` + # the Workbook is automatically closed when getting out the block defined by the with statement + print('pop:\n', pop) + print('\nbirths:\n', births) + print('\ndeaths:\n', deaths) + + id: 3 + +- markdown: | +
+ **Warning:** `open_excel` requires to work on Windows and to have the library ``xlwings`` installed. +
- markdown: | - or Excel sheets + The `HDF5` file format is specifically designed to store and organize large amounts of data. + Reading and writing data in this file format is much faster than with CSV or Excel. + An HDF5 file can contain multiple arrays, each array being associated with a key. + To read an array from an HDF5 file, you must use the ``read_hdf`` function and provide the key associated with the array: + + +- code: | + filepath_hdf = get_example_filepath('examples.h5') - ```python - # loads array from the first sheet if no sheet is given - pop = read_excel('demography.xlsx', 'pop') - ``` + # read the array from the file 'examples.h5' associated with the key 'pop' + pop = read_hdf(filepath_hdf, 'pop') + pop + id: 4 - markdown: | - or HDF5 files (HDF5 is file format designed to store and organize large amounts of data. - An HDF5 file can contain multiple arrays. + ### Dumping Arrays - Basic Usage (CSV, Excel, HDF5) - ```python - mortality = read_hdf('demography.h5','qx') - ``` + To write an array in a CSV file, you must use the ``to_csv`` method: + +- code: | + # save the array pop in the file 'pop.csv' + pop.to_csv('pop.csv') + + id: 5 - markdown: | - See documentation of reading functions for more details. + To write an array to a sheet of an Excel file, you can use the ``to_excel`` method: + +- code: | + # save the array pop in the sheet 'pop' of the Excel file 'population.xlsx' + pop.to_excel('population.xlsx', 'pop') + + id: 6 - markdown: | - ### Load Sessions + Note that ``to_excel`` create a new Excel file if it does not exist yet. + If the file already exists, a new sheet is added after the existing ones if that sheet does not already exists: + +- code: | + # add a new sheet 'births' to the file 'population.xlsx' and save the array births in it + births.to_excel('population.xlsx', 'births') + + id: 7 - markdown: | - The advantage of sessions is that you can load many arrays in one shot: + To reset an Excel file, you simply need to set the `overwrite_file` argument as True: + + +- code: | + # 1. reset the file 'population.xlsx' (all sheets are removed) + # 2. create a sheet 'pop' and save the array pop in it + pop.to_excel('population.xlsx', 'pop', overwrite_file=True) + + id: 8 + +- markdown: | + The ``open_excel`` function in combination with the ``dump()`` method allows you to open a Workbook and to export several arrays at once. If the Excel file doesn't exist, the ``overwrite_file`` argument must be set to True. - ```python - # this load several arrays from a single Excel file (each array is stored on a different sheet) - s = Session() - s.load('test.xlsx') - # or - s = Session('test.xlsx') +
+ **Warning:** The ``save`` method must be called at the end of the block defined by the *with* statement to actually write data in the Excel file, otherwise you will end up with an empty file. +
+ + +- code: | + # to create a new Excel file, argument overwrite_file must be set to True + with open_excel('population.xlsx', overwrite_file=True) as wb: + # add a new sheet 'pop' and dump the array pop in it + wb['pop'] = pop.dump() + # add a new sheet 'births' and dump the array births in it + wb['births'] = births.dump() + # add a new sheet 'deaths' and dump the array deaths in it + wb['deaths'] = deaths.dump() + # actually write data in the Workbook + wb.save() + + # the Workbook is automatically closed when getting out the block defined by the with statement + + id: 9 + +- markdown: | + To write an array in an HDF5 file, you must use the ``read_hdf`` function and provide the key that will be associated with the array: + + +- code: | + # save the array pop in the file 'population.h5' and associate it with the key 'pop' + pop.to_hdf('population.h5', 'pop') + + id: 10 + +- markdown: | + ### Specifying Wide VS Narrow format (CSV, Excel) + + By default, all reading functions assume that arrays are stored in the ``wide`` format, meaning that their last axis is represented horizontally: - # this load several arrays from a single HDF5 file (which is a very fast format) - s = Session() - s.load('test.h5') - # or - s = Session('test.h5') - ``` + | geo\time | 2013 | 2014 | 2015 | + | -------- | -------- | -------- | -------- | + | Belgium | 11137974 | 11180840 | 11237274 | + | France | 65600350 | 65942267 | 66456279 | + + By setting the ``wide`` argument to False, reading functions will assume instead that arrays are stored in the ``narrow`` format, i.e. one column per axis plus one value column: + + | geo | time | value | + | ------- | ---- | -------- | + | Belgium | 2013 | 11137974 | + | Belgium | 2014 | 11180840 | + | Belgium | 2015 | 11237274 | + | France | 2013 | 65600350 | + | France | 2014 | 65942267 | + | France | 2015 | 66456279 | + +- code: | + # set 'wide' argument to False to indicate that the array is stored in the 'narrow' format + pop_BE_FR = read_csv(csv_dir + '/pop_narrow_format.csv', wide=False) + pop_BE_FR + + id: 11 + +- code: | + # same for the read_excel function + pop_BE_FR = read_excel(filepath_excel, sheet='pop_narrow_format', wide=False) + pop_BE_FR + + id: 12 - markdown: | - ## Dump to CSV, Excel or HDF5 files + By default, writing functions will set the name of the column containing the data to 'value'. You can choose the name of this column by using the ``value_name`` argument. For example, using ``value_name='population'`` you can export the previous array as: - Arrays can be dumped in CSV files + | geo | time | population | + | ------- | ---- | ---------- | + | Belgium | 2013 | 11137974 | + | Belgium | 2014 | 11180840 | + | Belgium | 2015 | 11237274 | + | France | 2013 | 65600350 | + | France | 2014 | 65942267 | + | France | 2015 | 66456279 | + + +- code: | + # dump the array pop_BE_FR in a narrow format (one column per axis plus one value column). + # By default, the name of the column containing data is set to 'value' + pop_BE_FR.to_csv('pop_narrow_format.csv', wide=False) - ```python - household.to_csv('hh2.csv') - ``` + # same but replace 'value' by 'population' + pop_BE_FR.to_csv('pop_narrow_format.csv', wide=False, value_name='population') + + id: 13 + +- code: | + # same for the to_excel method + pop_BE_FR.to_excel('population.xlsx', 'pop_narrow_format', wide=False, value_name='population') + + id: 14 + +- markdown: | + Like with the ``to_excel`` method, it is possible to export arrays in a ``narrow`` format using ``open_excel``. + To do so, you must set the ``wide`` argument of the ``dump`` method to False: +- code: | + with open_excel('population.xlsx') as wb: + # dump the array pop_BE_FR in a narrow format: + # one column per axis plus one value column. + # Argument value_name can be used to change the name of the + # column containing the data (default name is 'value') + wb['pop_narrow_format'] = pop_BE_FR.dump(wide=False, value_name='population') + # don't forget to call save() + wb.save() + + # in the sheet 'pop_narrow_format', data is written as: + # | geo | time | value | + # | ------- | ---- | -------- | + # | Belgium | 2013 | 11137974 | + # | Belgium | 2014 | 11180840 | + # | Belgium | 2015 | 11237274 | + # | France | 2013 | 65600350 | + # | France | 2014 | 65942267 | + # | France | 2015 | 66456279 | + + id: 15 + - markdown: | - or in Excel files + ### Specifying Position in Sheet (Excel) - ```python - # if the file does not already exist, it is created with a single sheet, - # otherwise a new sheet is added to it - household.to_excel('demography_2.xlsx', overwrite_file=True) - # it is usually better to specify the sheet explicitly (by name or position) though - household.to_excel('demography_2.xlsx', 'hh') - ``` + If you want to read an array from an Excel sheet which does not start at cell `A1` (when there is more than one array stored in the same sheet for example), you will need to use the ``range`` argument. Note that this argument is only available if you have the library ``xlwings`` installed. + + +- code: | + # the 'range' argument must be used to load data not starting at cell A1. + # This is useful when there is several arrays stored in the same sheet + births = read_excel(filepath_excel, sheet='pop_births_deaths', range='A9:E15') + births + id: 16 - markdown: | - or in HDF5 files + Using ``open_excel``, ranges are passed in brackets: + + +- code: | + with open_excel(filepath_excel) as wb: + # store sheet 'pop_births_deaths' in a temporary variable sh + sh = wb['pop_births_deaths'] + # load the array pop from range A1:E7 + pop = sh['A1:E7'].load() + # load the array births from range A9:E15 + births = sh['A9:E15'].load() + # load the array deaths from range A17:E23 + deaths = sh['A17:E23'].load() - ```python - household.to_hdf('demography_2.h5', 'hh') - ``` + # the Workbook is automatically closed when getting out the block defined by the with statement + print('pop:\n', pop) + print('\nbirths:\n', births) + print('\ndeaths:\n', deaths) + id: 17 - markdown: | - See documentation of writing methods for more details. + When exporting arrays to Excel files, data is written starting at cell `A1` by default. Using the ``position`` argument of the ``to_excel`` method, it is possible to specify the top left cell of the dumped data. This can be useful when you want to export several arrays in the same sheet for example: +- code: | + filename = 'population.xlsx' + sheetname = 'pop_births_deaths' + + # save the arrays pop, births and deaths in the same sheet 'pop_births_and_deaths'. + # The 'position' argument is used to shift the location of the second and third arrays to be dumped + pop.to_excel(filename, sheetname) + births.to_excel(filename, sheetname, position='A9') + deaths.to_excel(filename, sheetname, position='A17') + + id: 18 + - markdown: | - ### Dump Sessions + Using ``open_excel``, the position is passed in brackets (this allows you to also add extra informations): + +- code: | + with open_excel('population.xlsx') as wb: + # add a new sheet 'pop_births_deaths' and write 'population' in the first cell + # note: you can use wb['new_sheet_name'] = '' to create an empty sheet + wb['pop_births_deaths'] = 'population' + # store sheet 'pop_births_deaths' in a temporary variable sh + sh = wb['pop_births_deaths'] + # dump the array pop in sheet 'pop_births_deaths' starting at cell A2 + sh['A2'] = pop.dump() + # add 'births' in cell A10 + sh['A10'] = 'births' + # dump the array births in sheet 'pop_births_deaths' starting at cell A11 + sh['A11'] = births.dump() + # add 'deaths' in cell A19 + sh['A19'] = 'deaths' + # dump the array deaths in sheet 'pop_births_deaths' starting at cell A20 + sh['A20'] = deaths.dump() + # don't forget to call save() + wb.save() + + # the Workbook is automatically closed when getting out the block defined by the with statement + + id: 19 - markdown: | - The advantage of sessions is that you can save many arrays in one shot: + ### Exporting data without headers (Excel) + + For some reasons, you may want to export only the data of an array without axes. For example, you may want to insert a new column containing extra information. As an exercise, let us consider we want to add the capital city for each country present in the array containing the total population by country: + + | country | capital city | 2013 | 2014 | 2015 | + | ------- | ------------ | -------- | -------- | -------- | + | Belgium | Brussels | 11137974 | 11180840 | 11237274 | + | France | Paris | 65600350 | 65942267 | 66456279 | + | Germany | Berlin | 80523746 | 80767463 | 81197537 | + + Assuming you have prepared an excel sheet as below: - ```python - # this saves all the arrays in a single excel file (each array on a different sheet) - s.save('test.xlsx') + | country | capital city | 2013 | 2014 | 2015 | + | ------- | ------------ | -------- | -------- | -------- | + | Belgium | Brussels | | | | + | France | Paris | | | | + | Germany | Berlin | | | || - # this saves all the arrays in a single HDF5 file (which is a very fast format) - s.save('test.h5') - ``` + you can then dump the data at right place by setting the ``header`` argument of ``to_excel`` to False and specifying the position of the data in sheet: + + +- code: | + pop_by_country = pop.sum('gender') + + # export only the data of the array pop_by_country starting at cell C2 + pop_by_country.to_excel('population.xlsx', 'pop_by_country', header=False, position='C2') + + id: 20 +- markdown: | + Using ``open_excel``, you can easily prepare the sheet and then export only data at the right place by either setting the ``header`` argument of the ``dump`` method to False or avoiding to call ``dump``: + + +- code: | + with open_excel('population.xlsx') as wb: + # create new empty sheet 'pop_by_country' + wb['pop_by_country'] = '' + # store sheet 'pop_by_country' in a temporary variable sh + sh = wb['pop_by_country'] + # write extra information (description) + sh['A1'] = 'Population at 1st January by country' + # export column names + sh['A2'] = ['country', 'capital city'] + sh['C2'] = pop_by_country.time.labels + # export countries as first column + sh['A3'].options(transpose=True).value = pop_by_country.geo.labels + # export capital cities as second column + sh['B3'].options(transpose=True).value = ['Brussels', 'Paris', 'Berlin'] + # export only data of pop_by_country + sh['C3'] = pop_by_country.dump(header=False) + # or equivalently + sh['C3'] = pop_by_country + # don't forget to call save() + wb.save() + + # the Workbook is automatically closed when getting out the block defined by the with statement + + id: 21 - markdown: | - ## Interact with Excel files + ### Specifying the Number of Axes at Reading (CSV, Excel) + + By default, ``read_csv`` and ``read_excel`` will search the position of the first cell containing the special character ``\`` in the header line in order to determine the number of axes of the array to read. The special character ``\`` is used to separate the name of the two last axes. If there is no special character ``\``, ``read_csv`` and ``read_excel`` will consider that the array to read has only one dimension. For an array stored as: + + | geo | gender\time | 2013 | 2014 | 2015 | + | ------- | ----------- | -------- | -------- | -------- | + | Belgium | Male | 5472856 | 5493792 | 5524068 | + | Belgium | Female | 5665118 | 5687048 | 5713206 | + | France | Male | 31772665 | 31936596 | 32175328 | + | France | Female | 33827685 | 34005671 | 34280951 | + | Germany | Male | 39380976 | 39556923 | 39835457 | + | Germany | Female | 41142770 | 41210540 | 41362080 | + + ``read_csv`` and ``read_excel`` will find the special character ``\`` in the second cell meaning it expects three axes (geo, gender and time). + + Sometimes, you need to read an array for which the name of the last axis is implicit: + + | geo | gender | 2013 | 2014 | 2015 | + | ------- | ------ | -------- | -------- | -------- | + | Belgium | Male | 5472856 | 5493792 | 5524068 | + | Belgium | Female | 5665118 | 5687048 | 5713206 | + | France | Male | 31772665 | 31936596 | 32175328 | + | France | Female | 33827685 | 34005671 | 34280951 | + | Germany | Male | 39380976 | 39556923 | 39835457 | + | Germany | Female | 41142770 | 41210540 | 41362080 | + + For such case, you will have to inform ``read_csv`` and ``read_excel`` of the number of axes of the output array by setting the ``nb_axes`` argument: + + +- code: | + # read the 3 x 2 x 3 array stored in the file 'pop_missing_axis_name.csv' wihout using 'nb_axes' argument. + pop = read_csv(csv_dir + '/pop_missing_axis_name.csv') + # shape and data type of the output array are not what we expected + pop.info + + id: 22 + +- code: | + # by setting the 'nb_axes' argument, you can indicate to read_csv the number of axes of the output array + pop = read_csv(csv_dir + '/pop_missing_axis_name.csv', nb_axes=3) + + # give a name to the last axis + pop = pop.rename(-1, 'time') + + # shape and data type of the output array are what we expected + pop.info + id: 23 + +- code: | + # same for the read_excel function + pop = read_excel(filepath_excel, sheet='pop_missing_axis_name', nb_axes=3) + pop = pop.rename(-1, 'time') + pop.info + + id: 24 - markdown: | - ### Write Arrays + ### NaNs and Missing Data Handling at Reading (CSV, Excel) - Open an Excel file + Sometimes, there is no data available for some label combinations. In the example below, the rows corresponding to `France - Male` and `Germany - Female` are missing: - ```python - wb = open_excel('test.xlsx', overwrite_file=True) - ``` + | geo | gender\time | 2013 | 2014 | 2015 | + | ------- | ----------- | -------- | -------- | -------- | + | Belgium | Male | 5472856 | 5493792 | 5524068 | + | Belgium | Female | 5665118 | 5687048 | 5713206 | + | France | Female | 33827685 | 34005671 | 34280951 | + | Germany | Male | 39380976 | 39556923 | 39835457 | + + By default, ``read_csv`` and ``read_excel`` will fill cells associated with missing label combinations with nans. + Be aware that, in that case, an int array will be converted to a float array. + +- code: | + # by default, cells associated will missing label combinations are filled with nans. + # In that case, the output array is converted to a float array + read_csv(csv_dir + '/pop_missing_values.csv') + + id: 25 + +- markdown: | + However, it is possible to choose which value to use to fill missing cells using the ``fill_value`` argument: + + +- code: | + read_csv(csv_dir + '/pop_missing_values.csv', fill_value=0) + + id: 26 + +- code: | + # same for the read_excel function + read_excel(filepath_excel, sheet='pop_missing_values', fill_value=0) + + id: 27 - markdown: | - Put an array in an Excel Sheet, **excluding** headers (labels) + ### Sorting Axes at Reading (CSV, Excel, HDF5) - ```python - # put arr at A1 in Sheet1, excluding headers (labels) - wb['Sheet1'] = arr - # same but starting at A9 - # note that Sheet1 must exist - wb['Sheet1']['A9'] = arr - ``` + The ``sort_rows`` and ``sort_columns`` arguments of the reading functions allows you to sort rows and columns alphabetically: + +- code: | + # sort labels at reading --> Male and Female labels are inverted + read_csv(csv_dir + '/pop.csv', sort_rows=True) + + id: 28 + +- code: | + read_excel(filepath_excel, sheet='births', sort_rows=True) + + id: 29 + +- code: | + read_hdf(filepath_hdf, key='deaths', sort_rows=True) + + id: 30 - markdown: | - Put an array in an Excel Sheet, **including** headers (labels) + ### Metadata (HDF5) + + Since the version 0.29 of LArray, it is possible to add metadata to arrays: + + +- code: | + pop.meta.title = 'Population at 1st January' + pop.meta.origin = 'Table demo_jpan from Eurostat' - ```python - # dump arr at A1 in Sheet2, including headers (labels) - wb['Sheet2'] = arr.dump() - # same but starting at A10 - wb['Sheet2']['A10'] = arr.dump() - ``` + pop.info + id: 31 - markdown: | - Save file to disk + These metadata are automatically saved and loaded when working with the HDF5 file format: + + +- code: | + pop.to_hdf('population.h5', 'pop') - ```python - wb.save() - ``` + new_pop = read_hdf('population.h5', 'pop') + new_pop.info + + id: 32 + +- markdown: | +
+ **Warning:** Currently, metadata associated with arrays cannot be saved and loaded when working with CSV and Excel files. + This restriction does not apply however to metadata associated with sessions. +
- markdown: | - Close file + ## Loading and Dumping Sessions - ```python - wb.close() - ``` + One of the main advantages of grouping arrays, axes and groups in session objects is that you can load and save all of them in one shot. Like arrays, it is possible to associate metadata to a session. These can be saved and loaded in all file formats. - markdown: | - ### Read Arrays + ### Loading Sessions (CSV, Excel, HDF5) - Open an Excel file + To load the items of a session, you have two options: - ```python - wb = open_excel('test.xlsx') - ``` + 1) Instantiate a new session and pass the path to the Excel/HDF5 file or to the directory containing CSV files to the Session constructor: -- markdown: | - Load an array from a sheet (assuming the presence of (correctly formatted) headers and only one array in sheet) +- code: | + # create a new Session object and load all arrays, axes, groups and metadata + # from all CSV files located in the passed directory + csv_dir = get_example_filepath('population_session') + session = Session(csv_dir) + + # create a new Session object and load all arrays, axes, groups and metadata + # stored in the passed Excel file + filepath_excel = get_example_filepath('population_session.xlsx') + session = Session(filepath_excel) - ```python - # save one array in Sheet3 (including headers) - wb['Sheet3'] = arr.dump() + # create a new Session object and load all arrays, axes, groups and metadata + # stored in the passed HDF5 file + filepath_hdf = get_example_filepath('population_session.h5') + session = Session(filepath_hdf) - # load array from the data starting at A1 in Sheet3 - arr = wb['Sheet3'].load() - ``` + print(session.summary()) + id: 33 - markdown: | - Load an array with its axes information from a range + 2) Call the ``load`` method on an existing session and pass the path to the Excel/HDF5 file or to the directory containing CSV files as first argument: + + +- code: | + # create a session containing 3 axes, 2 groups and one array 'pop' + filepath = get_example_filepath('pop_only.xlsx') + session = Session(filepath) - ```python - # if you need to use the same sheet several times, - # you can create a sheet variable - sheet2 = wb['Sheet2'] + print(session.summary()) + + id: 34 + +- code: | + # call the load method on the previous session and add the 'births' and 'deaths' arrays to it + filepath = get_example_filepath('births_and_deaths.xlsx') + session.load(filepath) - # load array contained in the 4 x 4 table defined by cells A10 and D14 - arr2 = sheet2['A10:D14'].load() - ``` + print(session.summary()) + id: 35 - markdown: | - ### Read Ranges (experimental) + The ``load`` method offers some options: - Load an array (raw data) with no axis information from a range + 1) Using the ``names`` argument, you can specify which items to load: + + +- code: | + session = Session() + + # use the names argument to only load births and deaths arrays + session.load(filepath_hdf, names=['births', 'deaths']) + + print(session.summary()) + + id: 36 + +- markdown: | + 2) Setting the ``display`` argument to True, the ``load`` method will print a message each time a new item is loaded: + + +- code: | + session = Session() - ```python - arr3 = wb['Sheet1']['A1:B4'] - ``` + # with display=True, the load method will print a message + # each time a new item is loaded + session.load(filepath_hdf, display=True) + id: 37 - markdown: | - in fact, this is not really an LArray ... + ### Dumping Sessions (CSV, Excel, HDF5) + + To save a session, you need to call the ``save`` method. The first argument is the path to a Excel/HDF5 file or to a directory if items are saved to CSV files: + + +- code: | + # save items of a session in CSV files. + # Here, the save method will create a 'population' directory in which CSV files will be written + session.save('population') + + # save session to an HDF5 file + session.save('population.h5') - ```python - type(arr3) + # save session to an Excel file + session.save('population.xlsx') - larray.io.excel.Range - ``` + # display the sheets contained in the file 'population.xlsx' + with open_excel('population.xlsx') as wb: + print(wb.sheet_names()) + + id: 38 + +- markdown: | +
+ **Note:** Concerning the CSV and Excel formats: + +
  • all Axis objects are saved together in the same Excel sheet (CSV file) named __axes__(.csv)
  • +
  • all Group objects are saved together in the same Excel sheet (CSV file) named __groups__(.csv)
  • +
  • metadata is saved in one Excel sheet (CSV file) named __metadata__(.csv)
  • +
    + These sheet (CSV file) names cannot be changed. +
    - markdown: | - ... but it can be used as such + The ``save`` method has several arguments: - ```python - arr3.sum(axis=0) - ``` + 1) Using the ``names`` argument, you can specify which items to save: +- code: | + # use the names argument to only save births and deaths arrays + session.save('population.xlsx', names=['births', 'deaths']) + + # display the sheets contained in the file 'population.xlsx' + with open_excel('population.xlsx') as wb: + print(wb.sheet_names()) + + id: 39 + - markdown: | - ... and it can be used for other stuff, like setting the formula instead of the value: + 2) By default, dumping a session to an Excel or HDF5 file will overwrite it. By setting the ``overwrite`` argument to False, you can choose to update the existing Excel or HDF5 file: + + +- code: | + pop = read_csv('./population/pop.csv') + ses_pop = Session([('pop', pop)]) - ```python - arr3.formula = '=D10+1' - ``` + # by setting overwrite to False, the destination file is updated instead of overwritten. + # The items already stored in the file but not present in the session are left intact. + # On the contrary, the items that exist in both the file and the session are completely overwritten. + ses_pop.save('population.xlsx', overwrite=False) + + # display the sheets contained in the file 'population.xlsx' + with open_excel('population.xlsx') as wb: + print(wb.sheet_names()) + id: 40 - markdown: | - In the future, we should also be able to set font name, size, style, etc. + 3) Setting the ``display`` argument to True, the ``save`` method will print a message each time an item is dumped: + +- code: | + # with display=True, the save method will print a message + # each time an item is dumped + session.save('population.xlsx', display=True) + + id: 41 # The lines below here may be deleted if you do not need them. # --------------------------------------------------------------------------- @@ -273,4 +748,21 @@ nbformat_minor: 2 # --------------------------------------------------------------------------- data: - [{execution_count: null, outputs: []}, {execution_count: null, outputs: []}] + [{execution_count: null, outputs: []}, {execution_count: null, outputs: []}, {execution_count: null, + outputs: []}, {execution_count: null, outputs: []}, {execution_count: null, outputs: []}, + {execution_count: null, outputs: []}, {execution_count: null, outputs: []}, {execution_count: null, + outputs: []}, {execution_count: null, outputs: []}, {execution_count: null, outputs: []}, + {execution_count: null, outputs: []}, {execution_count: null, outputs: []}, {execution_count: null, + outputs: []}, {execution_count: null, outputs: []}, {execution_count: null, outputs: []}, + {execution_count: null, outputs: []}, {execution_count: null, outputs: []}, {execution_count: null, + outputs: []}, {execution_count: null, outputs: []}, {execution_count: null, outputs: []}, + {execution_count: null, outputs: []}, {execution_count: null, outputs: []}, {execution_count: null, + outputs: []}, {execution_count: null, outputs: []}, {execution_count: null, outputs: []}, + {execution_count: null, outputs: []}, {execution_count: null, outputs: []}, {execution_count: null, + outputs: []}, {execution_count: null, outputs: []}, {execution_count: null, outputs: []}, + {execution_count: null, outputs: []}, {execution_count: null, outputs: []}, {execution_count: null, + outputs: []}, {execution_count: null, outputs: []}, {execution_count: null, outputs: []}, + {execution_count: null, outputs: []}, {execution_count: null, outputs: []}, {execution_count: null, + outputs: []}, {execution_count: null, outputs: []}, {execution_count: null, outputs: []}, + {execution_count: null, outputs: []}, {execution_count: null, outputs: []}] + diff --git a/doc/source/tutorial/tutorial_IO.ipynb b/doc/source/tutorial/tutorial_IO.ipynb index 7e47370ef..51bf03c3a 100644 --- a/doc/source/tutorial/tutorial_IO.ipynb +++ b/doc/source/tutorial/tutorial_IO.ipynb @@ -4,22 +4,25 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Load/Dump Arrays And Sessions From/To Files\n" + "# Load And Dump Arrays, Sessions, Axes And Groups\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Import the LArray library:\n" + "LArray provides methods and functions to load and dump LArray, Session, Axis Group objects to several formats such as Excel, CSV and HDF5. The HDF5 file format is designed to store and organize large amounts of data. It allows to read and write data much faster than when working with CSV and Excel files. \n" ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "nbsphinx": "hidden" + }, "outputs": [], "source": [ + "# first of all, import the LArray library\n", "from larray import *" ] }, @@ -27,312 +30,984 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Load from CVS, Excel or HDF5 files\n", + "## Loading and Dumping Arrays\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Loading Arrays - Basic Usage (CSV, Excel, HDF5)\n", "\n", - "Arrays can be loaded from CSV files\n", + "To read an array from a CSV file, you must use the ``read_csv`` function:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "csv_dir = get_example_filepath('examples')\n", "\n", - "```python\n", - "# read_tsv is a shortcut when data are separated by tabs instead of commas (default separator of read_csv)\n", - "# read_eurostat is a shortcut to read EUROSTAT TSV files\n", - "household = read_csv('hh.csv')\n", - "```\n" + "# read the array pop from the file 'pop.csv'.\n", + "# The data of the array below is derived from a subset of the demo_pjan table from Eurostat\n", + "pop = read_csv(csv_dir + '/pop.csv')\n", + "pop" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "or Excel sheets\n", + "To read an array from a sheet of an Excel file, you can use the ``read_excel`` function:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "filepath_excel = get_example_filepath('examples.xlsx')\n", "\n", - "```python\n", - "# loads array from the first sheet if no sheet is given\n", - "pop = read_excel('demography.xlsx', 'pop')\n", - "```\n" + "# read the array from the sheet 'pop' of the Excel file 'examples.xlsx'\n", + "pop = read_excel(filepath_excel, 'pop')\n", + "pop" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "or HDF5 files (HDF5 is file format designed to store and organize large amounts of data.\n", - "An HDF5 file can contain multiple arrays.\n", + "The ``open_excel`` function in combination with the ``load`` method allows you to load several arrays from the same Workbook without opening and closing it several times:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# open the Excel file 'population.xlsx' and let it opened as long as you keep the indent.\n", + "# The Python keyword ``with`` ensures that the Excel file is properly closed even if an error occurs\n", + "with open_excel(filepath_excel) as wb:\n", + " # load the array 'pop' from the sheet 'pop' \n", + " pop = wb['pop'].load()\n", + " # load the array 'births' from the sheet 'births'\n", + " # The data of the array below is derived from a subset of the demo_fasec table from Eurostat\n", + " births = wb['births'].load()\n", + " # load the array 'deaths' from the sheet 'deaths'\n", + " # The data of the array below is derived from a subset of the demo_magec table from Eurostat\n", + " deaths = wb['deaths'].load()\n", "\n", - "```python\n", - "mortality = read_hdf('demography.h5','qx')\n", - "```\n" + "# the Workbook is automatically closed when getting out the block defined by the with statement\n", + "print('pop:\\n', pop)\n", + "print('\\nbirths:\\n', births)\n", + "print('\\ndeaths:\\n', deaths)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "See documentation of reading functions for more details.\n" + "
    \n", + " **Warning:** `open_excel` requires to work on Windows and to have the library ``xlwings`` installed.\n", + "
    " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Load Sessions\n" + "The `HDF5` file format is specifically designed to store and organize large amounts of data. \n", + "Reading and writing data in this file format is much faster than with CSV or Excel. \n", + "An HDF5 file can contain multiple arrays, each array being associated with a key.\n", + "To read an array from an HDF5 file, you must use the ``read_hdf`` function and provide the key associated with the array:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "filepath_hdf = get_example_filepath('examples.h5')\n", + "\n", + "# read the array from the file 'examples.h5' associated with the key 'pop'\n", + "pop = read_hdf(filepath_hdf, 'pop')\n", + "pop" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "The advantage of sessions is that you can load many arrays in one shot:\n", + "### Dumping Arrays - Basic Usage (CSV, Excel, HDF5)\n", "\n", - "```python\n", - "# this load several arrays from a single Excel file (each array is stored on a different sheet)\n", - "s = Session()\n", - "s.load('test.xlsx')\n", - "# or \n", - "s = Session('test.xlsx')\n", + "To write an array in a CSV file, you must use the ``to_csv`` method:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# save the array pop in the file 'pop.csv'\n", + "pop.to_csv('pop.csv')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To write an array to a sheet of an Excel file, you can use the ``to_excel`` method:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# save the array pop in the sheet 'pop' of the Excel file 'population.xlsx' \n", + "pop.to_excel('population.xlsx', 'pop')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that ``to_excel`` create a new Excel file if it does not exist yet. \n", + "If the file already exists, a new sheet is added after the existing ones if that sheet does not already exists:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# add a new sheet 'births' to the file 'population.xlsx' and save the array births in it\n", + "births.to_excel('population.xlsx', 'births')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To reset an Excel file, you simply need to set the `overwrite_file` argument as True:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 1. reset the file 'population.xlsx' (all sheets are removed)\n", + "# 2. create a sheet 'pop' and save the array pop in it\n", + "pop.to_excel('population.xlsx', 'pop', overwrite_file=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The ``open_excel`` function in combination with the ``dump()`` method allows you to open a Workbook and to export several arrays at once. If the Excel file doesn't exist, the ``overwrite_file`` argument must be set to True.\n", "\n", - "# this load several arrays from a single HDF5 file (which is a very fast format)\n", - "s = Session()\n", - "s.load('test.h5')\n", - "# or \n", - "s = Session('test.h5')\n", - "```\n" + "
    \n", + " **Warning:** The ``save`` method must be called at the end of the block defined by the *with* statement to actually write data in the Excel file, otherwise you will end up with an empty file.\n", + "
    \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# to create a new Excel file, argument overwrite_file must be set to True\n", + "with open_excel('population.xlsx', overwrite_file=True) as wb:\n", + " # add a new sheet 'pop' and dump the array pop in it \n", + " wb['pop'] = pop.dump()\n", + " # add a new sheet 'births' and dump the array births in it \n", + " wb['births'] = births.dump()\n", + " # add a new sheet 'deaths' and dump the array deaths in it \n", + " wb['deaths'] = deaths.dump()\n", + " # actually write data in the Workbook\n", + " wb.save()\n", + " \n", + "# the Workbook is automatically closed when getting out the block defined by the with statement" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To write an array in an HDF5 file, you must use the ``read_hdf`` function and provide the key that will be associated with the array:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# save the array pop in the file 'population.h5' and associate it with the key 'pop'\n", + "pop.to_hdf('population.h5', 'pop')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Dump to CSV, Excel or HDF5 files\n", + "### Specifying Wide VS Narrow format (CSV, Excel)\n", "\n", - "Arrays can be dumped in CSV files\n", + "By default, all reading functions assume that arrays are stored in the ``wide`` format, meaning that their last axis is represented horizontally:\n", "\n", - "```python\n", - "household.to_csv('hh2.csv')\n", - "```\n" + "| geo\\time | 2013 | 2014 | 2015 |\n", + "| -------- | -------- | -------- | -------- |\n", + "| Belgium | 11137974 | 11180840 | 11237274 |\n", + "| France | 65600350 | 65942267 | 66456279 |\n", + "\n", + "By setting the ``wide`` argument to False, reading functions will assume instead that arrays are stored in the ``narrow`` format, i.e. one column per axis plus one value column:\n", + "\n", + "| geo | time | value |\n", + "| ------- | ---- | -------- |\n", + "| Belgium | 2013 | 11137974 |\n", + "| Belgium | 2014 | 11180840 |\n", + "| Belgium | 2015 | 11237274 |\n", + "| France | 2013 | 65600350 |\n", + "| France | 2014 | 65942267 |\n", + "| France | 2015 | 66456279 |\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# set 'wide' argument to False to indicate that the array is stored in the 'narrow' format\n", + "pop_BE_FR = read_csv(csv_dir + '/pop_narrow_format.csv', wide=False)\n", + "pop_BE_FR" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# same for the read_excel function\n", + "pop_BE_FR = read_excel(filepath_excel, sheet='pop_narrow_format', wide=False)\n", + "pop_BE_FR" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "or in Excel files\n", + "By default, writing functions will set the name of the column containing the data to 'value'. You can choose the name of this column by using the ``value_name`` argument. For example, using ``value_name='population'`` you can export the previous array as:\n", "\n", - "```python\n", - "# if the file does not already exist, it is created with a single sheet,\n", - "# otherwise a new sheet is added to it\n", - "household.to_excel('demography_2.xlsx', overwrite_file=True)\n", - "# it is usually better to specify the sheet explicitly (by name or position) though\n", - "household.to_excel('demography_2.xlsx', 'hh')\n", - "```\n" + "| geo | time | population |\n", + "| ------- | ---- | ---------- |\n", + "| Belgium | 2013 | 11137974 |\n", + "| Belgium | 2014 | 11180840 |\n", + "| Belgium | 2015 | 11237274 |\n", + "| France | 2013 | 65600350 |\n", + "| France | 2014 | 65942267 |\n", + "| France | 2015 | 66456279 |\n" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ - "or in HDF5 files\n", + "# dump the array pop_BE_FR in a narrow format (one column per axis plus one value column).\n", + "# By default, the name of the column containing data is set to 'value'\n", + "pop_BE_FR.to_csv('pop_narrow_format.csv', wide=False)\n", "\n", - "```python\n", - "household.to_hdf('demography_2.h5', 'hh')\n", - "```\n" + "# same but replace 'value' by 'population'\n", + "pop_BE_FR.to_csv('pop_narrow_format.csv', wide=False, value_name='population')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# same for the to_excel method\n", + "pop_BE_FR.to_excel('population.xlsx', 'pop_narrow_format', wide=False, value_name='population')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "See documentation of writing methods for more details.\n" + "Like with the ``to_excel`` method, it is possible to export arrays in a ``narrow`` format using ``open_excel``. \n", + "To do so, you must set the ``wide`` argument of the ``dump`` method to False:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open_excel('population.xlsx') as wb:\n", + " # dump the array pop_BE_FR in a narrow format: \n", + " # one column per axis plus one value column.\n", + " # Argument value_name can be used to change the name of the \n", + " # column containing the data (default name is 'value')\n", + " wb['pop_narrow_format'] = pop_BE_FR.dump(wide=False, value_name='population')\n", + " # don't forget to call save()\n", + " wb.save()\n", + "\n", + "# in the sheet 'pop_narrow_format', data is written as:\n", + "# | geo | time | value |\n", + "# | ------- | ---- | -------- |\n", + "# | Belgium | 2013 | 11137974 |\n", + "# | Belgium | 2014 | 11180840 |\n", + "# | Belgium | 2015 | 11237274 |\n", + "# | France | 2013 | 65600350 |\n", + "# | France | 2014 | 65942267 |\n", + "# | France | 2015 | 66456279 |" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Dump Sessions\n" + "### Specifying Position in Sheet (Excel)\n", + "\n", + "If you want to read an array from an Excel sheet which does not start at cell `A1` (when there is more than one array stored in the same sheet for example), you will need to use the ``range`` argument. Note that this argument is only available if you have the library ``xlwings`` installed. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# the 'range' argument must be used to load data not starting at cell A1.\n", + "# This is useful when there is several arrays stored in the same sheet\n", + "births = read_excel(filepath_excel, sheet='pop_births_deaths', range='A9:E15')\n", + "births" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "The advantage of sessions is that you can save many arrays in one shot:\n", + "Using ``open_excel``, ranges are passed in brackets:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open_excel(filepath_excel) as wb:\n", + " # store sheet 'pop_births_deaths' in a temporary variable sh\n", + " sh = wb['pop_births_deaths']\n", + " # load the array pop from range A1:E7\n", + " pop = sh['A1:E7'].load()\n", + " # load the array births from range A9:E15\n", + " births = sh['A9:E15'].load()\n", + " # load the array deaths from range A17:E23\n", + " deaths = sh['A17:E23'].load()\n", "\n", - "```python\n", - "# this saves all the arrays in a single excel file (each array on a different sheet)\n", - "s.save('test.xlsx')\n", + "# the Workbook is automatically closed when getting out the block defined by the with statement\n", + "print('pop:\\n', pop)\n", + "print('\\nbirths:\\n', births)\n", + "print('\\ndeaths:\\n', deaths)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When exporting arrays to Excel files, data is written starting at cell `A1` by default. Using the ``position`` argument of the ``to_excel`` method, it is possible to specify the top left cell of the dumped data. This can be useful when you want to export several arrays in the same sheet for example:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "filename = 'population.xlsx'\n", + "sheetname = 'pop_births_deaths'\n", "\n", - "# this saves all the arrays in a single HDF5 file (which is a very fast format)\n", - "s.save('test.h5')\n", - "```\n" + "# save the arrays pop, births and deaths in the same sheet 'pop_births_and_deaths'.\n", + "# The 'position' argument is used to shift the location of the second and third arrays to be dumped\n", + "pop.to_excel(filename, sheetname)\n", + "births.to_excel(filename, sheetname, position='A9')\n", + "deaths.to_excel(filename, sheetname, position='A17')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Interact with Excel files\n" + "Using ``open_excel``, the position is passed in brackets (this allows you to also add extra informations): \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open_excel('population.xlsx') as wb:\n", + " # add a new sheet 'pop_births_deaths' and write 'population' in the first cell\n", + " # note: you can use wb['new_sheet_name'] = '' to create an empty sheet\n", + " wb['pop_births_deaths'] = 'population'\n", + " # store sheet 'pop_births_deaths' in a temporary variable sh\n", + " sh = wb['pop_births_deaths']\n", + " # dump the array pop in sheet 'pop_births_deaths' starting at cell A2\n", + " sh['A2'] = pop.dump()\n", + " # add 'births' in cell A10\n", + " sh['A10'] = 'births'\n", + " # dump the array births in sheet 'pop_births_deaths' starting at cell A11 \n", + " sh['A11'] = births.dump()\n", + " # add 'deaths' in cell A19\n", + " sh['A19'] = 'deaths'\n", + " # dump the array deaths in sheet 'pop_births_deaths' starting at cell A20\n", + " sh['A20'] = deaths.dump()\n", + " # don't forget to call save()\n", + " wb.save()\n", + " \n", + "# the Workbook is automatically closed when getting out the block defined by the with statement" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Write Arrays\n", + "### Exporting data without headers (Excel)\n", + "\n", + "For some reasons, you may want to export only the data of an array without axes. For example, you may want to insert a new column containing extra information. As an exercise, let us consider we want to add the capital city for each country present in the array containing the total population by country:\n", + "\n", + "| country | capital city | 2013 | 2014 | 2015 |\n", + "| ------- | ------------ | -------- | -------- | -------- |\n", + "| Belgium | Brussels | 11137974 | 11180840 | 11237274 |\n", + "| France | Paris | 65600350 | 65942267 | 66456279 |\n", + "| Germany | Berlin | 80523746 | 80767463 | 81197537 |\n", + "\n", + "Assuming you have prepared an excel sheet as below: \n", "\n", - "Open an Excel file\n", + "| country | capital city | 2013 | 2014 | 2015 |\n", + "| ------- | ------------ | -------- | -------- | -------- |\n", + "| Belgium | Brussels | | | |\n", + "| France | Paris | | | |\n", + "| Germany | Berlin | | | ||\n", "\n", - "```python\n", - "wb = open_excel('test.xlsx', overwrite_file=True)\n", - "```\n" + "you can then dump the data at right place by setting the ``header`` argument of ``to_excel`` to False and specifying the position of the data in sheet:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pop_by_country = pop.sum('gender')\n", + "\n", + "# export only the data of the array pop_by_country starting at cell C2\n", + "pop_by_country.to_excel('population.xlsx', 'pop_by_country', header=False, position='C2')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Using ``open_excel``, you can easily prepare the sheet and then export only data at the right place by either setting the ``header`` argument of the ``dump`` method to False or avoiding to call ``dump``:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with open_excel('population.xlsx') as wb:\n", + " # create new empty sheet 'pop_by_country'\n", + " wb['pop_by_country'] = ''\n", + " # store sheet 'pop_by_country' in a temporary variable sh\n", + " sh = wb['pop_by_country']\n", + " # write extra information (description)\n", + " sh['A1'] = 'Population at 1st January by country'\n", + " # export column names\n", + " sh['A2'] = ['country', 'capital city']\n", + " sh['C2'] = pop_by_country.time.labels\n", + " # export countries as first column\n", + " sh['A3'].options(transpose=True).value = pop_by_country.geo.labels\n", + " # export capital cities as second column\n", + " sh['B3'].options(transpose=True).value = ['Brussels', 'Paris', 'Berlin']\n", + " # export only data of pop_by_country\n", + " sh['C3'] = pop_by_country.dump(header=False)\n", + " # or equivalently\n", + " sh['C3'] = pop_by_country\n", + " # don't forget to call save()\n", + " wb.save()\n", + " \n", + "# the Workbook is automatically closed when getting out the block defined by the with statement" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Put an array in an Excel Sheet, **excluding** headers (labels)\n", + "### Specifying the Number of Axes at Reading (CSV, Excel)\n", + "\n", + "By default, ``read_csv`` and ``read_excel`` will search the position of the first cell containing the special character ``\\`` in the header line in order to determine the number of axes of the array to read. The special character ``\\`` is used to separate the name of the two last axes. If there is no special character ``\\``, ``read_csv`` and ``read_excel`` will consider that the array to read has only one dimension. For an array stored as:\n", + "\n", + "| geo | gender\\time | 2013 | 2014 | 2015 |\n", + "| ------- | ----------- | -------- | -------- | -------- |\n", + "| Belgium | Male | 5472856 | 5493792 | 5524068 |\n", + "| Belgium | Female | 5665118 | 5687048 | 5713206 |\n", + "| France | Male | 31772665 | 31936596 | 32175328 |\n", + "| France | Female | 33827685 | 34005671 | 34280951 |\n", + "| Germany | Male | 39380976 | 39556923 | 39835457 |\n", + "| Germany | Female | 41142770 | 41210540 | 41362080 |\n", + "\n", + "``read_csv`` and ``read_excel`` will find the special character ``\\`` in the second cell meaning it expects three axes (geo, gender and time). \n", + "\n", + "Sometimes, you need to read an array for which the name of the last axis is implicit: \n", + "\n", + "| geo | gender | 2013 | 2014 | 2015 |\n", + "| ------- | ------ | -------- | -------- | -------- |\n", + "| Belgium | Male | 5472856 | 5493792 | 5524068 |\n", + "| Belgium | Female | 5665118 | 5687048 | 5713206 |\n", + "| France | Male | 31772665 | 31936596 | 32175328 |\n", + "| France | Female | 33827685 | 34005671 | 34280951 |\n", + "| Germany | Male | 39380976 | 39556923 | 39835457 |\n", + "| Germany | Female | 41142770 | 41210540 | 41362080 |\n", + "\n", + "For such case, you will have to inform ``read_csv`` and ``read_excel`` of the number of axes of the output array by setting the ``nb_axes`` argument:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# read the 3 x 2 x 3 array stored in the file 'pop_missing_axis_name.csv' wihout using 'nb_axes' argument.\n", + "pop = read_csv(csv_dir + '/pop_missing_axis_name.csv')\n", + "# shape and data type of the output array are not what we expected\n", + "pop.info" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# by setting the 'nb_axes' argument, you can indicate to read_csv the number of axes of the output array\n", + "pop = read_csv(csv_dir + '/pop_missing_axis_name.csv', nb_axes=3)\n", "\n", - "```python\n", - "# put arr at A1 in Sheet1, excluding headers (labels)\n", - "wb['Sheet1'] = arr\n", - "# same but starting at A9\n", - "# note that Sheet1 must exist\n", - "wb['Sheet1']['A9'] = arr\n", - "```\n" + "# give a name to the last axis\n", + "pop = pop.rename(-1, 'time')\n", + "\n", + "# shape and data type of the output array are what we expected\n", + "pop.info" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# same for the read_excel function\n", + "pop = read_excel(filepath_excel, sheet='pop_missing_axis_name', nb_axes=3)\n", + "pop = pop.rename(-1, 'time')\n", + "pop.info" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Put an array in an Excel Sheet, **including** headers (labels)\n", + "### NaNs and Missing Data Handling at Reading (CSV, Excel)\n", "\n", - "```python\n", - "# dump arr at A1 in Sheet2, including headers (labels)\n", - "wb['Sheet2'] = arr.dump()\n", - "# same but starting at A10\n", - "wb['Sheet2']['A10'] = arr.dump()\n", - "```\n" + "Sometimes, there is no data available for some label combinations. In the example below, the rows corresponding to `France - Male` and `Germany - Female` are missing:\n", + "\n", + "| geo | gender\\time | 2013 | 2014 | 2015 |\n", + "| ------- | ----------- | -------- | -------- | -------- |\n", + "| Belgium | Male | 5472856 | 5493792 | 5524068 |\n", + "| Belgium | Female | 5665118 | 5687048 | 5713206 |\n", + "| France | Female | 33827685 | 34005671 | 34280951 |\n", + "| Germany | Male | 39380976 | 39556923 | 39835457 |\n", + "\n", + "By default, ``read_csv`` and ``read_excel`` will fill cells associated with missing label combinations with nans. \n", + "Be aware that, in that case, an int array will be converted to a float array." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# by default, cells associated will missing label combinations are filled with nans.\n", + "# In that case, the output array is converted to a float array\n", + "read_csv(csv_dir + '/pop_missing_values.csv')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, it is possible to choose which value to use to fill missing cells using the ``fill_value`` argument:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "read_csv(csv_dir + '/pop_missing_values.csv', fill_value=0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# same for the read_excel function\n", + "read_excel(filepath_excel, sheet='pop_missing_values', fill_value=0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Save file to disk\n", + "### Sorting Axes at Reading (CSV, Excel, HDF5)\n", "\n", - "```python\n", - "wb.save()\n", - "```\n" + "The ``sort_rows`` and ``sort_columns`` arguments of the reading functions allows you to sort rows and columns alphabetically:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# sort labels at reading --> Male and Female labels are inverted\n", + "read_csv(csv_dir + '/pop.csv', sort_rows=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "read_excel(filepath_excel, sheet='births', sort_rows=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "read_hdf(filepath_hdf, key='deaths', sort_rows=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Close file\n", + "### Metadata (HDF5)\n", "\n", - "```python\n", - "wb.close()\n", - "```\n" + "Since the version 0.29 of LArray, it is possible to add metadata to arrays:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pop.meta.title = 'Population at 1st January'\n", + "pop.meta.origin = 'Table demo_jpan from Eurostat'\n", + "\n", + "pop.info" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Read Arrays\n", + "These metadata are automatically saved and loaded when working with the HDF5 file format: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pop.to_hdf('population.h5', 'pop')\n", "\n", - "Open an Excel file\n", + "new_pop = read_hdf('population.h5', 'pop')\n", + "new_pop.info" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
    \n", + " **Warning:** Currently, metadata associated with arrays cannot be saved and loaded when working with CSV and Excel files.\n", + " This restriction does not apply however to metadata associated with sessions.\n", + "
    " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Loading and Dumping Sessions\n", "\n", - "```python\n", - "wb = open_excel('test.xlsx')\n", - "```\n" + "One of the main advantages of grouping arrays, axes and groups in session objects is that you can load and save all of them in one shot. Like arrays, it is possible to associate metadata to a session. These can be saved and loaded in all file formats. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Load an array from a sheet (assuming the presence of (correctly formatted) headers and only one array in sheet)\n", + "### Loading Sessions (CSV, Excel, HDF5)\n", "\n", - "```python\n", - "# save one array in Sheet3 (including headers)\n", - "wb['Sheet3'] = arr.dump()\n", + "To load the items of a session, you have two options:\n", "\n", - "# load array from the data starting at A1 in Sheet3\n", - "arr = wb['Sheet3'].load()\n", - "```\n" + "1) Instantiate a new session and pass the path to the Excel/HDF5 file or to the directory containing CSV files to the Session constructor:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# create a new Session object and load all arrays, axes, groups and metadata \n", + "# from all CSV files located in the passed directory\n", + "csv_dir = get_example_filepath('population_session')\n", + "session = Session(csv_dir)\n", + "\n", + "# create a new Session object and load all arrays, axes, groups and metadata\n", + "# stored in the passed Excel file\n", + "filepath_excel = get_example_filepath('population_session.xlsx')\n", + "session = Session(filepath_excel)\n", + "\n", + "# create a new Session object and load all arrays, axes, groups and metadata\n", + "# stored in the passed HDF5 file\n", + "filepath_hdf = get_example_filepath('population_session.h5')\n", + "session = Session(filepath_hdf)\n", + "\n", + "print(session.summary())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Load an array with its axes information from a range\n", + "2) Call the ``load`` method on an existing session and pass the path to the Excel/HDF5 file or to the directory containing CSV files as first argument:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# create a session containing 3 axes, 2 groups and one array 'pop'\n", + "filepath = get_example_filepath('pop_only.xlsx')\n", + "session = Session(filepath)\n", "\n", - "```python\n", - "# if you need to use the same sheet several times,\n", - "# you can create a sheet variable\n", - "sheet2 = wb['Sheet2']\n", + "print(session.summary())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# call the load method on the previous session and add the 'births' and 'deaths' arrays to it\n", + "filepath = get_example_filepath('births_and_deaths.xlsx')\n", + "session.load(filepath)\n", "\n", - "# load array contained in the 4 x 4 table defined by cells A10 and D14\n", - "arr2 = sheet2['A10:D14'].load()\n", - "```\n" + "print(session.summary())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Read Ranges (experimental)\n", + "The ``load`` method offers some options:\n", "\n", - "Load an array (raw data) with no axis information from a range\n", + "1) Using the ``names`` argument, you can specify which items to load:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "session = Session()\n", + "\n", + "# use the names argument to only load births and deaths arrays\n", + "session.load(filepath_hdf, names=['births', 'deaths'])\n", + "\n", + "print(session.summary())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2) Setting the ``display`` argument to True, the ``load`` method will print a message each time a new item is loaded: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "session = Session()\n", "\n", - "```python\n", - "arr3 = wb['Sheet1']['A1:B4']\n", - "```\n" + "# with display=True, the load method will print a message\n", + "# each time a new item is loaded\n", + "session.load(filepath_hdf, display=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "in fact, this is not really an LArray ...\n", + "### Dumping Sessions (CSV, Excel, HDF5)\n", "\n", - "```python\n", - "type(arr3)\n", + "To save a session, you need to call the ``save`` method. The first argument is the path to a Excel/HDF5 file or to a directory if items are saved to CSV files:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# save items of a session in CSV files.\n", + "# Here, the save method will create a 'population' directory in which CSV files will be written \n", + "session.save('population')\n", + "\n", + "# save session to an HDF5 file\n", + "session.save('population.h5')\n", "\n", - "larray.io.excel.Range\n", - "```\n" + "# save session to an Excel file\n", + "session.save('population.xlsx')\n", + "\n", + "# display the sheets contained in the file 'population.xlsx'\n", + "with open_excel('population.xlsx') as wb:\n", + " print(wb.sheet_names())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
    \n", + " **Note:** Concerning the CSV and Excel formats: \n", + " \n", + "
  • all Axis objects are saved together in the same Excel sheet (CSV file) named __axes__(.csv)
  • \n", + "
  • all Group objects are saved together in the same Excel sheet (CSV file) named __groups__(.csv)
  • \n", + "
  • metadata is saved in one Excel sheet (CSV file) named __metadata__(.csv)
  • \n", + "
    \n", + " These sheet (CSV file) names cannot be changed. \n", + "
    " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "... but it can be used as such\n", + "The ``save`` method has several arguments:\n", + "\n", + "1) Using the ``names`` argument, you can specify which items to save:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# use the names argument to only save births and deaths arrays\n", + "session.save('population.xlsx', names=['births', 'deaths'])\n", "\n", - "```python\n", - "arr3.sum(axis=0)\n", - "```\n" + "# display the sheets contained in the file 'population.xlsx'\n", + "with open_excel('population.xlsx') as wb:\n", + " print(wb.sheet_names())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "... and it can be used for other stuff, like setting the formula instead of the value:\n", + "2) By default, dumping a session to an Excel or HDF5 file will overwrite it. By setting the ``overwrite`` argument to False, you can choose to update the existing Excel or HDF5 file: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pop = read_csv('./population/pop.csv')\n", + "ses_pop = Session([('pop', pop)])\n", "\n", - "```python\n", - "arr3.formula = '=D10+1'\n", - "```\n" + "# by setting overwrite to False, the destination file is updated instead of overwritten.\n", + "# The items already stored in the file but not present in the session are left intact. \n", + "# On the contrary, the items that exist in both the file and the session are completely overwritten.\n", + "ses_pop.save('population.xlsx', overwrite=False)\n", + "\n", + "# display the sheets contained in the file 'population.xlsx'\n", + "with open_excel('population.xlsx') as wb:\n", + " print(wb.sheet_names())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "In the future, we should also be able to set font name, size, style, etc.\n" + "3) Setting the ``display`` argument to True, the ``save`` method will print a message each time an item is dumped: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# with display=True, the save method will print a message\n", + "# each time an item is dumped\n", + "session.save('population.xlsx', display=True)" ] } ],