Skip to content

r-hyperspec/pyspc-write-jdx

Repository files navigation

Python module for writing JCAMP-DX spectra files

Introduction

The package is based on 5.01 specification of the JCAMP-DX format. The following options from the standard are not yet implemented:

  • Compressed number formats
  • Peak annotation data
  • XYDATA in its sequential (i.e. classic) format

Data records and data table records

These terms are used slightly diffrently from the original specification. The reason is that there is no clear difference between LDR (LINE-DATA-RECORD in the specification) containing meta-information and containing actual spectral/peak inforation. For that reason, in the package LDRs with meta-information are called 'data records' and LDRs with spectral/peak information are called 'data table record'

Single column format

The original specification does not provide an option to print data-table-records in long CSV style, i.e. each line contains a pair X,Y separated by a separator. However, this format technically fits to XYDATA and XYPOINTS formats if the line break is put after each X,Y pair instead of filling in 80 characters. We find this format useful, so this was added as a feature.

Installation

pip install git+https://github.com/r-hyperspec/pyspc-write-jdx

Examples

Simple JDX

Minial simple JDX:

from pyspc_write_jdx import SimpleJDX

jdx = SimpleJDX(
    title="Some title",
    data_type="INFRARED SPECTRUM",
    xunits="l/CM",
    origin="PySPC",
    owner="Me",
    xypoints=[[1, 2, 3], [4, 5, 6]],
    single_column=True, # This is default
    decimal_places=4, # This is default
)

# Get as a string
jdx.to_string()
# UserWarning: {
#     "yunits": [
#         "DATA-LABEL '##YUNITS=' is required."
#     ]
# }
#   warnings.warn(json.dumps(validations, indent=4))
# ##TITLE= Some title
# ##JCAMP-DX= 5.01
# ##DATA TYPE= INFRARED SPECTRUM
# ##XUNITS= l/CM
# ##YUNITS=
# ##FIRSTX= 1
# ##LASTX= 3
# ##XFACTOR= 1
# ##YFACTOR= 1
# ##NPOINTS= 3
# ##ORIGIN= PySPC
# ##OWNER= Me
# ##XYPOINTS= (XY..XY)
# 1.0000, 4.0000
# 2.0000, 5.0000
# 3.0000, 6.0000
# ##END=


# Save to a file
jdx.to_file("tmp.jdx")

Set some value after instance is created:

jdx.yunits.value = "ABSORBANCE"
jdx.yunits.comment = "You can even add a comment"
jdx.comments.value = "This is test comment"

jdx.to_string()
# ##TITLE= Some title
# ##JCAMP-DX= 5.01
# ##DATA TYPE= INFRARED SPECTRUM
# ##XUNITS= l/CM
# ##YUNITS= ABSORBANCE  $$ You can even add a comment
# ##FIRSTX= 1
# ##LASTX= 3
# ##XFACTOR= 1
# ##YFACTOR= 1
# ##NPOINTS= 3
# ##ORIGIN= PySPC
# ##OWNER= Me
# ##= This is test comment
# ##XYPOINTS= (XY..XY)
# 1.0000, 4.0000
# 2.0000, 5.0000
# 3.0000, 6.0000
# ##END=

Custom class to avoid arguments that are always the same (i.e. xunits, data_type, etc.):

import datetime
from pyspc_write_jdx import SimpleJDX
from pyspc_write_jdx.data_records import StringDataRecord

class MyCustomIRSimpleJDX(SimpleJDX):
    # Optinal: add your custom data record
    # NOTE: User defined data recod labels must start with $
    my_custom_record = StringDataRecord("$MY CUSTOM RECORD", choices=["A", "B"], required=True)

    # Optinal: specifiy output data records and/or change the order
    # NOTE: 'title' must be the first. All required data records must be presented
    output_data_records = [
        'title',
        'jcamp_dx',
        'data_type',
        'long_date',
        'my_custom_record',
        'xunits',
        'yunits',
        'firstx',
        'lastx',
        'firsty',
        'xfactor',
        'yfactor',
        'npoints',
        'origin',
        'owner',
    ]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.data_type.value = "INFRARED SPECTRUM"
        self.xunits.value = "l/CM"
        self.yunits.value = "ABSORBANCE"
        self.origin.value = "PySPC"
        self.origin.comment = "Generated by pyspc"
        self.owner.value = "Me"
        self.long_date.value = datetime.datetime.now()

# Try it out!s
jdx = MyCustomIRSimpleJDX(title="Some title", my_custom_record="A", xypoints=[[1, 2, 3], [4, 5, 6]])

# Get as a string
jdx.to_string()
# ##TITLE= Some title
# ##JCAMP-DX= 5.01
# ##DATA TYPE= INFRARED SPECTRUM
# ##LONG DATE= 2022/08/20 15:50:13.691015
# ##$MY CUSTOM RECORD= A
# ##XUNITS= l/CM
# ##YUNITS= ABSORBANCE
# ##FIRSTX= 1
# ##LASTX= 3
# ##XFACTOR= 1
# ##YFACTOR= 1
# ##NPOINTS= 3
# ##ORIGIN= PySPC  $$ Generated by pyspc
# ##OWNER= Me
# ##XYPOINTS= (XY..XY)
# 1.0000, 4.0000
# 2.0000, 5.0000
# 3.0000, 6.0000
# ##END=

Compund JDX

It is possible to generate multi-block JDX files:

from pyspc_write_jdx import CompoundJDX, SimpleJDX

# Create inner blocks with spectral data
jdx1 = SimpleJDX(title="Title1", xypoints=[[1, 2], [4, 5]])
jdx2 = SimpleJDX(title="Title2", xydata=[[10, 20], [40, 50]])

# Create compound JDX by providing inner simple JDXs
multi_block_jdx = CompoundJDX("This is big compound JDX", jdx1, jdx2)

# Save to a file
multi_block_jdx.to_file("test_multiblock.jdx")

# Print as a string
multi_block_jdx.to_string()
# ##TITLE= This is big compound JDX
# ##JCAMP-DX= 5.01
# ##DATA TYPE= LINK
# ##BLOCKS= 2

# ##TITLE= Title1
# ##JCAMP-DX= 5.01
# ##DATA TYPE=
# ##XUNITS=
# ##YUNITS=
# ##FIRSTX= 1
# ##LASTX= 2
# ##XFACTOR= 1
# ##YFACTOR= 1
# ##NPOINTS= 2
# ##ORIGIN=
# ##OWNER=
# ##XYPOINTS= (XY..XY)
# 1.0000, 4.0000
# 2.0000, 5.0000
# ##END=

# ##TITLE= Title2
# ##JCAMP-DX= 5.01
# ##DATA TYPE=
# ##XUNITS=
# ##YUNITS=
# ##FIRSTX= 10
# ##LASTX= 20
# ##XFACTOR= 1
# ##YFACTOR= 1
# ##NPOINTS= 2
# ##ORIGIN=
# ##OWNER=
# ##XYDATA= (X++(Y..Y))
# 10.0000 40.0000
# 20.0000 50.0000
# ##END=

# ##END=

It is also possible to add inner blocks dynamicaly:

# Create new data
jdx3 = SimpleJDX(title="Title3", xydata=[[100, 200], [400, 500]])
# Apped the new block
multi_block_jdx.add_block(jdx3)

About

python module for writing JCAMP-DX spectra files

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages