# Python Imports:
import numpy as np
import scipy.io as sio
%cd datafiles
!ls

"""
Explanation: Input/Output
Often, it is important to import information from a variety of sources and output the result. A few ways of creating and saving files are demonstrated.
By the end of this file you should have seen simple examples of:
1. Printing string output to the screen
2. Reading and writing string output to/from text files
3. Reading and writing string output to/from csv files
4. Reading and writing string output to/from binary files
5. Reading and writing string output to/from matlab files
Further reading:
http://docs.h5py.org/en/latest/index.html
End of explanation
"""


kb_contents = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
print(kb_contents)

"""
Explanation: From standard input/keyboard:
The import of simple text files can be performed directly in python via:
End of explanation
"""


# Read line by line:
file_obj = open('01-simpletext.txt','r')
for line in file_obj:
    print(line)
file_obj.close()

# Use the read method:
file_obj = open('01-simpletext.txt','r')
file_contents = file_obj.read()
file_obj.close()
print(file_contents)

# Python 'with' statement automatically takes care of the close for us:
with open('01-simpletext.txt','r') as file_obj:
    print(file_obj.read())

# Write to ascii files:
file_obj = open('01-simpletext_write.txt','w')
file_obj.write(file_contents)
file_obj.close()

# Or, alternatively:
with open('01-simpletext_write.txt','w') as file_obj:
    file_obj.write(file_contents)

# Check that our written output is good:
with open('01-simpletext_write.txt','r') as file_obj:
    print(file_obj.read())

"""
Explanation: Text (ascii) files:
The import of simple text files can be performed directly in python by creating a file object and operating on that object:
End of explanation
"""


# Creating a python list:
with open('01-simpledata.csv','r') as file_obj:
    file_contents = file_obj.read().split(',')
    
print(file_contents)

# Use numpy to read an array from a file
file_contents = np.loadtxt(open('01-simpledata.csv'), delimiter=",")
file_contents = file_contents.astype('float')
print(file_contents)

# Save output of numpy array to csv file
file_contents_write = file_contents*2 #Double to differentiate read vs write data

np.savetxt('01-simpledata_write.csv',file_contents_write, '%0.3f', delimiter=",") 
# %0.3f specifies scientific notation with 3 decimal places
file_contents = np.loadtxt(open('01-simpledata_write.csv'), delimiter=",")
print(file_contents)

"""
Explanation: Comma Separated Values (.csv files):
Here, we import data separated by a particular delimiter, as in tsv or csv files:
End of explanation
"""


# Read in the csv from the previous step:
file_contents = np.loadtxt(open('01-simpledata_write.csv'), delimiter=",")
print(file_contents)

# Save as a binary file:
np.savetxt('01-simpledata_write.bin', file_contents_write*2) # Note the lack of demiliter
file_contents = np.loadtxt('01-simpledata_write.bin')

# The following is not recommended, as it is platform dependent:
#np.ndarray.tofile(file_contents_write, '01-simpledata_write.bin')
#file_contents = np.fromfile('01-simpledata_write.bin')

print(file_contents)

"""
Explanation: Binary Files:
Binary files store the same information as text or csv, but do so directly in bytes, rather than using ascii to encode. They have the advantage of being faster to read and smaller in size, but are not readily readable by a typical text editor (notepad, vim, sublime, etc).
Note: be careful to avoid numpy.fromfile and numpy.tofile as they are not platform independent!
End of explanation
"""


# Use scipy to read in .mat files:
mat_contents= sio.loadmat('01-simplemat.mat')

testvar = mat_contents['testvar']
print(testvar)

# Use scipy to write .mat files:
testvar_write = testvar*2 # Double to make read data different from write data

sio.savemat('01-simplemat_write.mat' ,{'testvar_write':testvar_write})

mat_contents = sio.loadmat('01-simplemat_write.mat')
testvar = mat_contents['testvar_write']
print(testvar_write)

"""
Explanation: Matlab (.mat) files:
Generating matlab variables via:
testvar = magic(9)
save('01-simplemat.mat','testvar')
These can then be loaded via scipy.io (imported as sio here):
End of explanation
"""


import h5py

# Load csv data:
data_csv = np.loadtxt(open('01-simpledata_write.csv'), delimiter=",")

# Load mat data:
data_mat = sio.loadmat('01-simplemat_write.mat')['testvar_write']

# Load text data:
with open('01-simpletext.txt','r') as file_obj:
    data_txt = file_obj.read()

# Create a h5py file object:
with h5py.File("01-data_write.hdf5", "w") as file_obj:   
    # Use file_obj to create data sets
    
    # Create a dataset object and assign the values from data:
    dataset1 = file_obj.create_dataset("data", data = data_csv)    

"""
Explanation: HDF5 files
HDF5 or Hierarchical Data Format provides a file format that has a much greater amount of flexibility at the cost of a bit more complexity. HDF5 is ideal when there would otherwise have been many small files. There are two main objects:
- Groups: folder-like containers that work like Python dictionaries
- Datasets: NumPy-like arrays
End of explanation
"""


with h5py.File("01-data_write.hdf5", 'r') as file_obj:
    print(file_obj["data"].name)
    print(file_obj["data"].value)

"""
Explanation: Check that the data has been written to the file by opening it:
End of explanation
"""


# Open the same h5py file object:
with h5py.File("01-data_write.hdf5", "w") as file_obj:

    # Create a group object, and create datasets underneath it:
    grp_nums = file_obj.create_group("Numbers")
    dataset_csv = grp_nums.create_dataset("CSV", data=data_csv)
    dataset_mat = grp_nums.create_dataset("MAT", data=data_mat)

    # Create a second group object, and create datasets underneath it:
    grp_txt = file_obj.create_group("Text")
    txt_hf5 = np.asarray(data_txt, dtype="S") # Convert to NumPy S dtype:
    dataset_txt = grp_txt.create_dataset("lorem", data=txt_hf5)

"""
Explanation: The "Hierarchical" part of the HDF5 file format provides groups, which act like Python dictionaries or 'folders' for the various Datasets.
End of explanation
"""


def print_attrs(name, obj): # Function that prints the name and object
    print(name)
    print(obj)
        
with h5py.File("01-data_write.hdf5", 'r') as file_obj:
    file_obj.visititems(print_attrs) # Use .visititems to get info

with h5py.File("01-data_write.hdf5", 'r') as file_obj:
    print(file_obj["/Numbers/CSV"].name)
    print(file_obj["/Numbers/CSV"].value)
    
    print(file_obj["/Numbers/MAT"].name)
    print(file_obj["/Numbers/MAT"].value)
    
    print(file_obj["/Text/lorem"].name)
    print(file_obj["/Text/lorem"].value)

"""
Explanation: After saving this data, check the file structure:
End of explanation
"""


def print_attrs(name, obj):
    print(name)
    if isinstance(obj, h5py.Group):
        print(obj)
    if isinstance(obj, h5py.Dataset):
        print(obj.value)
        
with h5py.File("01-data_write.hdf5", 'r') as file_obj:
    file_obj.visititems(print_attrs)

"""
Explanation: For coinvenience, it's possible to print all of the information using .visititems:
End of explanation
"""


