from __future__ import annotations
from chisurf import typing
import csv
import os
import numpy as np
import chisurf
[docs]def save_xy(
filename: str,
x: np.ndarray,
y: np.ndarray,
verbose: bool = chisurf.verbose,
fmt: str = "%.3f\t%.3f",
header_string: str = None
) -> None:
"""
Saves data x, y to file in format (csv). x and y should have the same length.
:param filename: string
Target filename
:param x: array
:param y: array
:param verbose: bool
:param header_string:
:param fmt:
"""
if verbose:
print("Writing histogram to file: %s" % filename)
with chisurf.fio.zipped.open_maybe_zipped(
filename=filename,
mode='w'
) as fp:
if header_string is not None:
fp.write(header_string)
for p in zip(x, y):
fp.write(fmt % (p[0], p[1]))
[docs]def load_xy(
filename: str,
verbose: bool = chisurf.verbose,
usecols: typing.Tuple[int, int] = None,
skiprows: int = 0,
delimiter: str = "\t"
) -> typing.Tuple[
np.array,
np.array
]:
if usecols is None:
usecols = [0, 1]
if verbose:
print("Loading file: ", filename)
data = np.loadtxt(
filename,
skiprows=skiprows,
usecols=usecols,
delimiter=delimiter
)
return data.T[0], data.T[1]
[docs]class Csv(object):
"""
Csv is a class to handle coma separated value files.
:param kwargs:
Examples
--------
Two-column data
>>> import chisurf.fio.ascii
>>> csv = chisurf.fio.ascii.Csv(skiprows=10)
>>> filename = './test/data/tcspc/ibh_sample/Decay_577D.txt'
>>> csv.load(filename)
>>> csv.data
array([ 1.00000000e+00, 2.00000000e+00, 3.00000000e+00, ...,
4.09400000e+03, 4.09500000e+03, 4.09600000e+03])
>>> csv.data_y
array([ 0., 0., 0., ..., 0., 0., 0.])
>>> max(csv.data_y)
50010.0
One-column Jordi data
>>> csv = chisurf.fio.ascii.Csv(skiprows=11)
>>> filename = './test/data/tcspc/ibh_sample/Decay_577D.txt'
>>> csv.load(filename)
>>> csv.data_x
array([ 1.00000000e+00, 2.00000000e+00, 3.00000000e+00, ...,
4.09400000e+03, 4.09500000e+03, 4.09600000e+03])
>>> csv.data_y
array([ 0., 0., 0., ..., 0., 0., 0.])
>>> max(csv.data_y)
50010.0
"""
def __init__(
self,
*args,
filename: str = None,
colspecs: typing.List[int] = None,
use_header: bool = False,
x_on: bool = True,
y_on: bool = True,
col_x: int = 0,
col_y: int = 1,
col_ex: int = 2,
col_ey: int = 3,
reverse: bool = False,
error_x_on: bool = False,
directory: str = '.',
skiprows: int = 9,
verbose: bool = chisurf.verbose,
file_type: str = 'csv',
**kwargs
):
"""
:param args:
:param filename:
:param colspecs:
:param use_header:
:param x_on:
:param y_on:
:param col_x:
:param col_y:
:param col_ex:
:param col_ey:
:param reverse:
:param error_x_on:
:param directory:
:param skiprows:
:param verbose:
:param file_type:
:param kwargs:
"""
self._filename = filename
self.use_header = use_header
self.x_on = x_on
self.error_y_on = y_on
self.col_x = col_x
self.col_y = col_y
self.col_ex = col_ex
self.col_ey = col_ey
self.reverse = reverse
self.error_x_on = error_x_on
self.directory = directory
self.skiprows = skiprows
self.file_type = file_type
self.verbose = verbose
self._header = None
if colspecs is None:
colspecs = (15, 17, 17)
self.colspecs = colspecs
self._data = kwargs.get('data', None)
if isinstance(filename, str):
self.load(
filename
)
@property
def filename(
self
) -> str:
"""
The currently open filename (after setting this parameter the file is opened)
"""
return self._filename
[docs] def load(
self,
filename: str,
skiprows: int = None,
use_header: bool = None,
verbose: bool = chisurf.verbose,
delimiter: str = None,
file_type: str = None,
infer_delimiter: bool = True,
usecols: typing.List[int] = None,
**kwargs
) -> None:
"""
This method loads a filename to the `Csv` object
:param filename: string specifying the file
:param skiprows: number of rows to skip in the file. By default the
value of the instance is used
:param verbose: The method is verbose if verbose is set to True of
the verbose attribute of the instance is
True.
"""
if filename is None:
return None
if file_type is None:
file_type = self.file_type
if use_header is None:
use_header = self.use_header
if skiprows is None:
skiprows = self.skiprows
# process header
header = 'infer' if use_header else None
if use_header:
with chisurf.fio.zipped.open_maybe_zipped(
filename=filename,
mode='r'
) as fp:
for _ in range(skiprows):
fp.readline()
header_line = fp.readline()
self._header = header_line.split(delimiter)
skiprows += 1
if os.path.isfile(filename):
self.directory = os.path.dirname(filename)
self._filename = filename
colspecs = self.colspecs
if verbose:
print("Reading: {}".format(filename))
print("Skip rows: {}".format(skiprows))
print("Use header: {}".format(use_header))
with chisurf.fio.zipped.open_maybe_zipped(
filename=filename,
mode='r'
) as csvfile:
print(csvfile.read()[:512])
if file_type == 'csv':
if (delimiter is None) and infer_delimiter:
with chisurf.fio.zipped.open_maybe_zipped(
filename=filename,
mode='r'
) as csvfile:
for _ in range(skiprows):
csvfile.readline()
dialect = csv.Sniffer().sniff(
csvfile.read(), delimiters=';,|\t '
)
delimiter = dialect.delimiter
d = np.genfromtxt(
fname=filename,
delimiter=delimiter,
skip_header=skiprows,
usecols=usecols,
** kwargs
)
else:
d = np.genfromtxt(
skip_header=skiprows,
fname=filename,
delimiter=colspecs,
usecols=usecols,
names=header,
**kwargs
)
self._data = d
else:
chisurf.logging.warning("File %s not found" % filename)
[docs] def save(
self,
data: np.ndarray,
filename: str,
delimiter: str = '\t',
file_type: str = 'txt',
header: str = ''
):
self._data = data
if self.verbose:
s = """Saving
------
filename: %s
reading_routine: %s
delimiter: %s
Object-type: %s
""" % (filename, file_type, delimiter, type(data))
print(s)
# if isinstance(data, chisurf.curve.Curve):
# d = np.array(data[:])
# elif isinstance(data, np.ndarray):
# d = data
# else:
# d = np.array(data)
#
if file_type == 'txt':
np.savetxt(
filename,
data.T,
delimiter=delimiter,
header=header
)
if file_type == 'npy':
np.save(
filename,
data.T
)
@property
def n_cols(
self
) -> int:
"""
The number of columns
"""
return self._data.shape[1]
@property
def n_rows(
self
) -> int:
"""
The number of rows
"""
return self._data.shape[0]
@property
def data(
self
) -> np.array:
"""
Numpy array of the data
"""
if self.reverse:
return np.array(self._data, dtype=np.float64).T[::-1]
else:
return np.array(self._data, dtype=np.float64).T
@property
def header(
self
) -> typing.List[str]:
"""
A list of the column headers
"""
if self.use_header is not None:
header = self._header
else:
header = range(self.data.shape[1])
return [str(i) for i in header]
@property
def n_points(
self
) -> int:
"""
The number of data points corresponds to the number of rows
:py:attribute`.CSV.n_rows`
"""
return self.n_rows