from larray import *

"""
Explanation: Presenting LArray objects (Axis, Groups, Array, Session)
Import the LArray library:
End of explanation
"""


s = 1 + 2

# In the interactive mode, there is no need to use the print() function 
# to display the content of the variable 's'.
# Simply typing 's' is enough
s

# In the interactive mode, there is no need to use the print() function 
# to display the result of an expression
1 + 2

"""
Explanation: <div class="alert alert-warning">

**Note:** 
The tutorial is generated from Jupyter notebooks which work in the "interactive" mode (like in the LArray Editor console). 
In the interactive mode, there is no need to use the print() function to display the content of a variable.
Simply writing its name is enough. The same remark applies for the returned value of an expression.<br><br>
In a Python script (file with .py extension), you always need to use the print() function to display the content of 
a variable or the value returned by a function or an expression.

</div>
End of explanation
"""


# labels given as a list
time = Axis([2007, 2008, 2009, 2010], 'time')
# create an axis using one string
gender = Axis('gender=M,F')
# labels generated using the special syntax start..end
age = Axis('age=0..100')

time, gender, age

"""
Explanation: Axis
An Axis represents a dimension of an Array object.
It consists of a name and a list of labels. 
They are several ways to create an axis:
End of explanation
"""


# When a string is passed to the Axis() constructor, LArray will automatically infer the type of the labels
age = Axis("age=0..5")
age

# Mixing special characters like + with numbers will lead to create an axis with labels of type str instead of int.
age = Axis("age=0..4,5+")
age

"""
Explanation: <div class="alert alert-warning">

**Warning:**  
  When using the string syntax `"axis_name=list,of,labels"` or `"axis_name=start..end"`, LArray will automatically infer the type of labels.<br>
  For instance, the command line `age = Axis("age=0..100")` will create an age axis with labels of type `int`.<br><br>
  Mixing special characters like `+` with numbers will lead to create an axis with labels of type `str` instead of `int`.
  As a consequence, the command line `age = Axis("age=0..98,99+")` will create an age axis with labels of type `str` instead of `int`! 

</div>
End of explanation
"""


age = Axis('age=0..100')

# create an anonymous Group object 'teens'
teens = age[10:18]
teens

# create a Group object 'pensioners' with a name 
pensioners = age[67:] >> 'pensioners'
pensioners

"""
Explanation: See the Axis section of the API Reference to explore all methods of Axis objects.
Groups
A Group represents a selection of labels from an Axis. It can optionally have a name (using operator &gt;&gt;). 
Groups can be used when selecting a subset of an array and in aggregations. 
Group objects are created as follow:
End of explanation
"""


# method 'named' returns a new group with the given name
teens = teens.named('teens')

# operator >> is just a shortcut for the call of the method named
teens = teens >> 'teens'

teens

"""
Explanation: It is possible to set a name or to rename a group after its declaration:
End of explanation
"""


# mixing slices and individual labels leads to the creation of several groups (a tuple of groups)
age[0:10, 20, 30, 40]

# the union() method allows to mix slices and individual labels to create a single group
age[0:10].union(age[20, 30, 40])

"""
Explanation: <div class="alert alert-warning">

**Warning:** Mixing slices and individual labels inside the `[ ]` will generate **several groups** (a tuple of groups) instead of a single group.<br>If you want to create a single group using both slices and individual labels, you need to use the `.union()` method (see below).   

</div>
End of explanation
"""


# define axes
age = Axis('age=0-9,10-17,18-66,67+')
gender = Axis('gender=female,male')
time = Axis('time=2015..2017')
# list of the axes
axes = [age, gender, time]

# define some data. This is the belgian population (in thousands). Source: eurostat.
data = [[[633, 635, 634],
         [663, 665, 664]],
        [[484, 486, 491],
         [505, 511, 516]],
        [[3572, 3581, 3583],
         [3600, 3618, 3616]],
        [[1023, 1038, 1053],
         [756, 775, 793]]]

# metadata
meta = {'title': 'random array'}

arr = Array(data, axes, meta=meta)
arr

"""
Explanation: See the Group section of the API Reference to explore all methods of Group objects.
Array
An Array object represents a multidimensional array with labeled axes.
Create an array from scratch
To create an array from scratch, you need to provide the data and a list of axes. 
Optionally, metadata (title, description, creation date, authors, ...) can be associated to the array:
End of explanation
"""


arr.meta.description = 'array containing random values between 0 and 100'

arr.meta

"""
Explanation: Metadata can be added to an array at any time using:
End of explanation
"""


# start defines the starting value of data
ndtest((3, 3), start=-1)

# start defines the starting value of data
# label_start defines the starting index of labels
ndtest((3, 3), start=-1, label_start=2)

# empty generates uninitialised array with correct axes
# (much faster but use with care!).
# This not really random either, it just reuses a portion
# of memory that is available, with whatever content is there.
# Use it only if performance matters and make sure all data
# will be overridden.
empty([age, gender, time])

zeros([age, gender, time])

# dtype=int forces to store int data instead of default float
ones([age, gender, time], dtype=int)

full([age, gender, time], fill_value=1.23)

"""
Explanation: <div class="alert alert-warning">

**Warning:**  
  <ul>
    <li>Currently, only the HDF (.h5) file format supports saving and loading array metadata.</li>
    <li>Metadata is not kept when actions or methods are applied on an array
        except for operations modifying the object in-place, such as `population[age < 10] = 0`,
        and when the method `copy()` is called. Do not add metadata to an array if you know
        you will apply actions or methods on it before dumping it.</li>
  </ul>

</div>

Array creation functions
Arrays can also be generated in an easier way through creation functions:

ndtest : creates a test array with increasing numbers as data
empty : creates an array but leaves its allocated memory
   unchanged (i.e., it contains "garbage". Be careful !)
zeros: fills an array with 0
ones : fills an array with 1
full : fills an array with a given value
sequence : creates an array from an axis by iteratively applying a function to a given initial value.

Except for ndtest, a list of axes must be provided.
Axes can be passed in different ways:

as Axis objects
as integers defining the lengths of auto-generated wildcard axes
as a string : 'gender=M,F;time=2007,2008,2009' (name is optional)
as pairs (name, labels)

Optionally, the type of data stored by the array can be specified using argument dtype.
End of explanation
"""


ones_like(arr)

"""
Explanation: All the above functions exist in (func)_like variants which take axes from another array
End of explanation
"""


# With initial=1.0 and inc=0.5, we generate the sequence 1.0, 1.5, 2.0, 2.5, 3.0, ...
sequence(age, initial=1.0, inc=0.5)

"""
Explanation: Create an array using the special sequence function (see link to documention of sequence in API reference for more examples):
End of explanation
"""


# create a test array
ndtest([age, gender, time])

"""
Explanation: Inspecting Array objects
End of explanation
"""


arr.info

"""
Explanation: Get array summary : metadata + dimensions + description of axes + dtype + size in memory
End of explanation
"""


arr.axes

"""
Explanation: Get axes
End of explanation
"""


arr.ndim

"""
Explanation: Get number of dimensions
End of explanation
"""


arr.shape

"""
Explanation: Get length of each dimension
End of explanation
"""


arr.size

"""
Explanation: Get total number of elements of the array
End of explanation
"""


arr.dtype

"""
Explanation: Get type of internal data (int, float, ...)
End of explanation
"""


arr.memory_used

"""
Explanation: Get size in memory
End of explanation
"""


# extract the 'time' axis belonging to the 'arr' array
time = arr.time
time

"""
Explanation: Display the array in the viewer (graphical user interface) in read-only mode.
This will open a new window and block execution of the rest of code until the windows is closed! Required PyQt installed.
python
view(arr)
Or load it in Excel:
python
arr.to_excel()
Extract an axis from an array
It is possible to extract an axis belonging to an array using its name:
End of explanation
"""


gender = Axis("gender=Male,Female")
time = Axis("time=2013..2017")

# create an empty session
demography_session = Session()

# add axes to the session
demography_session.gender = gender
demography_session.time = time

# add arrays to the session
demography_session.population = zeros((gender, time))
demography_session.births = zeros((gender, time))
demography_session.deaths = zeros((gender, time))

# add metadata after creation
demography_session.meta.title = 'Demographic Model of Belgium'
demography_session.meta.description = 'Models the demography of Belgium'

# print content of the session
print(demography_session.summary())

"""
Explanation: More on Array objects
To know how to save and load arrays in CSV, Excel or HDF format, please refer to the Loading and Dumping Arrays section of the tutorial.
See the Array section of the API Reference to explore all methods of Array objects.
Session
A Session object is a dictionary-like object used to gather several arrays, axes and groups. 
A session is particularly adapted to gather all input objects of a model or to gather the output arrays from different scenarios. Like with arrays, it is possible to associate metadata to sessions.
Creating Sessions
To create a session, you can first create an empty session and then populate it with arrays, axes and groups:
End of explanation
"""


gender = Axis("gender=Male,Female")
time = Axis("time=2013..2017")

demography_session = Session(gender=gender, time=time, population=zeros((gender, time)), 
                     births=zeros((gender, time)), deaths=zeros((gender, time)), 
                     meta=Metadata(title='Demographic Model of Belgium', description='Modelize the demography of Belgium'))

# print content of the session
print(demography_session.summary())

"""
Explanation: or you can create and populate a session in one step:
End of explanation
"""


