# -*- coding: utf-8 -*-
"""
CSV Tabular Data Input / Output
===============================
Defines various input / output objects for *CSV* tabular data files:
- :func:`colour.read_spectral_data_from_csv_file`
- :func:`colour.read_sds_from_csv_file`
- :func:`colour.write_sds_to_csv_file`
"""
from __future__ import division, unicode_literals
from collections import OrderedDict
import csv
from colour.colorimetry import SpectralDistribution
from colour.constants import DEFAULT_FLOAT_DTYPE
__author__ = 'Colour Developers'
__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers'
__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause'
__maintainer__ = 'Colour Developers'
__email__ = 'colour-developers@colour-science.org'
__status__ = 'Production'
__all__ = [
'read_spectral_data_from_csv_file', 'read_sds_from_csv_file',
'write_sds_to_csv_file'
]
[docs]def read_spectral_data_from_csv_file(path,
delimiter=',',
fields=None,
default=0):
"""
Reads the spectral data from given *CSV* file in the following form:
390, 4.15003E-04, 3.68349E-04, 9.54729E-03
395, 1.05192E-03, 9.58658E-04, 2.38250E-02
400, 2.40836E-03, 2.26991E-03, 5.66498E-02
...
830, 9.74306E-07, 9.53411E-08, 0.00000
and returns it as an *OrderedDict* of *dict* as follows:
OrderedDict([
('field', {'wavelength': 'value', ..., 'wavelength': 'value'}),
...,
('field', {'wavelength': 'value', ..., 'wavelength': 'value'})])
Parameters
----------
path : unicode
Absolute *CSV* file path.
delimiter : unicode, optional
*CSV* file content delimiter.
fields : array_like, optional
*CSV* file spectral data fields names. If no value is provided the
first line of the file will be used as spectral data fields names.
default : numeric, optional
Default value for fields row with missing value.
Returns
-------
OrderedDict
*CSV* file content.
Raises
------
RuntimeError
If the *CSV* spectral data file doesn't define the appropriate fields.
Notes
-----
- A *CSV* spectral data file should define at least define two fields:
one for the wavelengths and one for the associated values of one
spectral distribution.
- If no value is provided for the fields names, the first line of the
file will be used as spectral data fields names.
Examples
--------
>>> import os
>>> from pprint import pprint
>>> csv_file = os.path.join(os.path.dirname(__file__), 'tests',
... 'resources', 'colorchecker_n_ohta.csv')
>>> sds_data = read_spectral_data_from_csv_file(csv_file)
>>> pprint(list(sds_data.keys()))
['1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
'13',
'14',
'15',
'16',
'17',
'18',
'19',
'20',
'21',
'22',
'23',
'24']
"""
with open(path, 'rU') as csv_file:
reader = csv.DictReader(
csv_file, delimiter=str(delimiter), fieldnames=fields)
if len(reader.fieldnames) == 1:
raise RuntimeError(('A "CSV" spectral data file should define '
'the following fields: '
'("wavelength", "field 1", ..., "field n")!'))
wavelength = reader.fieldnames[0]
fields = reader.fieldnames[1:]
data = OrderedDict(zip(fields, ({} for _ in range(len(fields)))))
for line in reader:
for field in fields:
try:
value = DEFAULT_FLOAT_DTYPE(line[field])
except ValueError:
value = default
data[field][DEFAULT_FLOAT_DTYPE(line[wavelength])] = value
return data
[docs]def read_sds_from_csv_file(path, delimiter=',', fields=None, default=0):
"""
Reads the spectral data from given *CSV* file and return its content as an
*OrderedDict* of :class:`colour.SpectralDistribution` classes.
Parameters
----------
path : unicode
Absolute *CSV* file path.
delimiter : unicode, optional
*CSV* file content delimiter.
fields : array_like, optional
*CSV* file spectral data fields names. If no value is provided the
first line of the file will be used for as spectral data fields names.
default : numeric
Default value for fields row with missing value.
Returns
-------
OrderedDict
:class:`colour.SpectralDistribution` classes of given *CSV* file.
Examples
--------
>>> from colour.utilities import numpy_print_options
>>> import os
>>> csv_file = os.path.join(os.path.dirname(__file__), 'tests',
... 'resources', 'colorchecker_n_ohta.csv')
>>> sds = read_sds_from_csv_file(csv_file)
>>> print(tuple(sds.keys()))
('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', \
'14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24')
>>> with numpy_print_options(suppress=True):
... sds['1'] # doctest: +ELLIPSIS
SpectralDistribution([[ 380. , 0.048],
[ 385. , 0.051],
[ 390. , 0.055],
[ 395. , 0.06 ],
[ 400. , 0.065],
[ 405. , 0.068],
[ 410. , 0.068],
[ 415. , 0.067],
[ 420. , 0.064],
[ 425. , 0.062],
[ 430. , 0.059],
[ 435. , 0.057],
[ 440. , 0.055],
[ 445. , 0.054],
[ 450. , 0.053],
[ 455. , 0.053],
[ 460. , 0.052],
[ 465. , 0.052],
[ 470. , 0.052],
[ 475. , 0.053],
[ 480. , 0.054],
[ 485. , 0.055],
[ 490. , 0.057],
[ 495. , 0.059],
[ 500. , 0.061],
[ 505. , 0.062],
[ 510. , 0.065],
[ 515. , 0.067],
[ 520. , 0.07 ],
[ 525. , 0.072],
[ 530. , 0.074],
[ 535. , 0.075],
[ 540. , 0.076],
[ 545. , 0.078],
[ 550. , 0.079],
[ 555. , 0.082],
[ 560. , 0.087],
[ 565. , 0.092],
[ 570. , 0.1 ],
[ 575. , 0.107],
[ 580. , 0.115],
[ 585. , 0.122],
[ 590. , 0.129],
[ 595. , 0.134],
[ 600. , 0.138],
[ 605. , 0.142],
[ 610. , 0.146],
[ 615. , 0.15 ],
[ 620. , 0.154],
[ 625. , 0.158],
[ 630. , 0.163],
[ 635. , 0.167],
[ 640. , 0.173],
[ 645. , 0.18 ],
[ 650. , 0.188],
[ 655. , 0.196],
[ 660. , 0.204],
[ 665. , 0.213],
[ 670. , 0.222],
[ 675. , 0.231],
[ 680. , 0.242],
[ 685. , 0.251],
[ 690. , 0.261],
[ 695. , 0.271],
[ 700. , 0.282],
[ 705. , 0.294],
[ 710. , 0.305],
[ 715. , 0.318],
[ 720. , 0.334],
[ 725. , 0.354],
[ 730. , 0.372],
[ 735. , 0.392],
[ 740. , 0.409],
[ 745. , 0.42 ],
[ 750. , 0.436],
[ 755. , 0.45 ],
[ 760. , 0.462],
[ 765. , 0.465],
[ 770. , 0.448],
[ 775. , 0.432],
[ 780. , 0.421]],
interpolator=SpragueInterpolator,
interpolator_args={},
extrapolator=Extrapolator,
extrapolator_args={...})
"""
data = read_spectral_data_from_csv_file(path, delimiter, fields, default)
sds = OrderedDict(((key, SpectralDistribution(value, name=key))
for key, value in data.items()))
return sds
[docs]def write_sds_to_csv_file(sds, path, delimiter=',', fields=None):
"""
Writes the given spectral distributions to given *CSV* file.
Parameters
----------
sds : dict
Spectral distributions to write.
path : unicode
Absolute *CSV* file path.
delimiter : unicode, optional
*CSV* file content delimiter.
fields : array_like, optional
*CSV* file spectral data fields names. If no value is provided the
order of fields will be the one defined by the sorted spectral
distributions *dict*.
Returns
-------
bool
Definition success.
Raises
------
RuntimeError
If the given spectral distributions have different shapes.
"""
if len(sds) != 1:
shapes = [sd.shape for sd in sds.values()]
if not all(shape == shapes[0] for shape in shapes):
raise RuntimeError(('Cannot write spectral distributions '
'with different shapes to "CSV" file!'))
wavelengths = tuple(sds.values())[0].wavelengths
with open(path, 'w') as csv_file:
fields = list(fields) if fields is not None else sorted(sds.keys())
writer = csv.DictWriter(
csv_file,
delimiter=str(delimiter),
fieldnames=['wavelength'] + fields,
lineterminator='\n')
writer.writeheader()
for wavelength in wavelengths:
row = {'wavelength': wavelength}
row.update(
dict((field, sds[field][wavelength]) for field in fields))
writer.writerow(row)
return True