Source code for colour.io.tabular

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
CSV Tabular Data Input / Output
===============================

Defines various input / output objects for *CSV* tabular data files:

-   :func:`read_spectral_data_from_csv_file`
-   :func:`read_spds_from_csv_file`
-   :func:`write_spds_to_csv_file`
"""

from __future__ import division, unicode_literals

import csv

from colour.colorimetry import SpectralPowerDistribution

__author__ = 'Colour Developers'
__copyright__ = 'Copyright (C) 2013 - 2014 - Colour Developers'
__license__ = 'New BSD License - http://opensource.org/licenses/BSD-3-Clause'
__maintainer__ = 'Colour Developers'
__email__ = '[email protected]'
__status__ = 'Production'

__all__ = ['read_spectral_data_from_csv_file',
           'read_spds_from_csv_file',
           'write_spds_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 ad *dict* of *dict* as follows: {'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 ------- dict *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 power 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') >>> spds_data = read_spectral_data_from_csv_file(csv_file) >>> pprint(sorted(spds_data.keys())) ['1', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '2', '20', '21', '22', '23', '24', '3', '4', '5', '6', '7', '8', '9'] """ 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 = dict(zip(fields, ({} for x in range(len(fields))))) for line in reader: for field in fields: try: value = float(line[field]) except ValueError: value = default data[field][float(line[wavelength])] = value return data
[docs]def read_spds_from_csv_file(path, delimiter=',', fields=None, default=0): """ Reads the spectral data from given *CSV* file and return its content as a *dict* of :class:`colour.colorimetry.spectrum.TriSpectralPowerDistribution` 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 ------- dict :class:`colour.colorimetry.spectrum.TriSpectralPowerDistribution` classes of given *CSV* file. Examples -------- >>> import os >>> from pprint import pprint >>> csv_file = os.path.join( ... os.path.dirname(__file__), ... 'tests', ... 'resources', ... 'colorchecker_n_ohta.csv') >>> spds = read_spds_from_csv_file(csv_file) >>> pprint(sorted(spds.items())) # doctest: +ELLIPSIS [('1', <...SpectralPowerDistribution object at 0x...>), ('10', <...SpectralPowerDistribution object at 0x...>), ('11', <...SpectralPowerDistribution object at 0x...>), ('12', <...SpectralPowerDistribution object at 0x...>), ('13', <...SpectralPowerDistribution object at 0x...>), ('14', <...SpectralPowerDistribution object at 0x...>), ('15', <...SpectralPowerDistribution object at 0x...>), ('16', <...SpectralPowerDistribution object at 0x...>), ('17', <...SpectralPowerDistribution object at 0x...>), ('18', <...SpectralPowerDistribution object at 0x...>), ('19', <...SpectralPowerDistribution object at 0x...>), ('2', <...SpectralPowerDistribution object at 0x...>), ('20', <...SpectralPowerDistribution object at 0x...>), ('21', <...SpectralPowerDistribution object at 0x...>), ('22', <...SpectralPowerDistribution object at 0x...>), ('23', <...SpectralPowerDistribution object at 0x...>), ('24', <...SpectralPowerDistribution object at 0x...>), ('3', <...SpectralPowerDistribution object at 0x...>), ('4', <...SpectralPowerDistribution object at 0x...>), ('5', <...SpectralPowerDistribution object at 0x...>), ('6', <...SpectralPowerDistribution object at 0x...>), ('7', <...SpectralPowerDistribution object at 0x...>), ('8', <...SpectralPowerDistribution object at 0x...>), ('9', <...SpectralPowerDistribution object at 0x...>)] """ data = read_spectral_data_from_csv_file(path, delimiter, fields, default) spds = dict(((key, SpectralPowerDistribution(key, value)) for key, value in data.items())) return spds
[docs]def write_spds_to_csv_file(spds, path, delimiter=',', fields=None): """ Writes the given spectral power distributions to given *CSV* file. Parameters ---------- spds : dict Spectral power 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 power distributions *dict*. Returns ------- bool Definition success. Raises ------ RuntimeError If the given spectral power distributions have different shapes. """ shapes = [spd.shape for spd in spds.values()] if not all(shape == shapes[0] for shape in shapes): raise RuntimeError(('Cannot write spectral power distributions with ' 'different shapes to "CSV" file!')) wavelengths = tuple(spds.values())[0].wavelengths with open(path, 'w') as csv_file: fields = list(fields) if fields is not None else sorted(spds.keys()) writer = csv.DictWriter(csv_file, delimiter=str(delimiter), fieldnames=['wavelength'] + fields) # Python 2.7.x / 3.4.x only. # writer.writeheader() writer.writerow(dict((name, name) for name in writer.fieldnames)) for wavelength in wavelengths: row = {'wavelength': wavelength} row.update( dict((field, spds[field][wavelength]) for field in fields)) writer.writerow(row) return True