axographio.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. """
  2. README
  3. ===============================================================================
  4. This is an adapter to represent axographio objects as neo objects.
  5. axographio is a file i/o Python module that can read in axograph ".axgx" files.
  6. It is available under a BSD-3-Clause license and can be installed from pip.
  7. The following file types are supported:
  8. - AXGX/AXGD (Axograph X file format)
  9. Based on stimfitio.pyfrom neo.io
  10. 11 JUL 2018, W. Hart, Swinburne University, Australia
  11. """
  12. # needed for python 3 compatibility
  13. from __future__ import absolute_import
  14. from datetime import datetime
  15. import os
  16. import sys
  17. import numpy as np
  18. import quantities as pq
  19. from neo.io.baseio import BaseIO
  20. from neo.core import Block, Segment, AnalogSignal
  21. try:
  22. import axographio
  23. except ImportError as err:
  24. HAS_AXOGRAPHIO = False
  25. AXOGRAPHIO_ERR = err
  26. else:
  27. HAS_AXOGRAPHIO = True
  28. AXOGRAPHIO_ERR = None
  29. class AxographIO(BaseIO):
  30. """
  31. Class for converting an Axographio object to a Neo object.
  32. Provides a standardized representation of the data as defined by the neo
  33. project; this is useful to explore the data with an increasing number of
  34. electrophysiology software tools that rely on the Neo standard.
  35. axographio is a file i/o Python module that can read in axograph ".axgx" files.
  36. It is available under a BSD-3-Clause license and can be installed from pip.
  37. The following file types are supported:
  38. - AXGX/AXGD (Axograph X file format)
  39. Example usage:
  40. >>> import neo
  41. >>> neo_obj = neo.io.AxographIO("file.axgx")
  42. or
  43. >>> import axographio
  44. >>> axo_obj = axographio.read("file.axgx")
  45. >>> neo_obj = neo.io.AxographIO(axo_obj)
  46. """
  47. is_readable = True
  48. is_writable = False
  49. supported_objects = [Block, Segment, AnalogSignal]
  50. readable_objects = [Block]
  51. writeable_objects = []
  52. has_header = False
  53. is_streameable = False
  54. read_params = {Block: []}
  55. write_params = None
  56. name = 'AXOGRAPH'
  57. extensions = ['axgx', 'axgd']
  58. mode = 'file'
  59. def __init__(self, filename=None):
  60. """
  61. Arguments:
  62. filename : Either a filename or an axographio object
  63. """
  64. if not HAS_AXOGRAPHIO:
  65. raise AXOGRAPHIO_ERR
  66. BaseIO.__init__(self)
  67. if hasattr(filename, 'lower'):
  68. self.filename = filename
  69. self.axo_obj = None
  70. else:
  71. self.axo_obj = filename
  72. self.filename = None
  73. def read_block(self, **kargs):
  74. if self.filename is not None:
  75. self.axo_obj = axographio.read(self.filename)
  76. # Build up the block
  77. blk = Block()
  78. blk.rec_datetime = None
  79. if self.filename is not None:
  80. # modified time is not ideal but less prone to
  81. # cross-platform issues than created time (ctime)
  82. blk.file_datetime = datetime.fromtimestamp(os.path.getmtime(self.filename))
  83. # store the filename if it is available
  84. blk.file_origin = self.filename
  85. # determine the channel names and counts
  86. _, channel_ordering = np.unique(self.axo_obj.names[1:], return_index=True)
  87. channel_names = np.array(self.axo_obj.names[1:])[np.sort(channel_ordering)]
  88. channel_count = len(channel_names)
  89. # determine the time signal and sample period
  90. sample_period = self.axo_obj.data[0].step * pq.s
  91. start_time = self.axo_obj.data[0].start * pq.s
  92. # Attempt to read units from the channel names
  93. channel_unit_names = [x.split()[-1].strip('()') for x in channel_names]
  94. channel_units = []
  95. for unit in channel_unit_names:
  96. try:
  97. channel_units.append(pq.Quantity(1, unit))
  98. except LookupError:
  99. channel_units.append(None)
  100. # Strip units from channel names
  101. channel_names = [' '.join(x.split()[:-1]) for x in channel_names]
  102. # build up segments by grouping axograph columns
  103. for seg_idx in range(1, len(self.axo_obj.data), channel_count):
  104. seg = Segment(index=seg_idx)
  105. # add in the channels
  106. for chan_idx in range(0, channel_count):
  107. signal = pq.Quantity(
  108. self.axo_obj.data[seg_idx + chan_idx], channel_units[chan_idx])
  109. analog = AnalogSignal(signal,
  110. sampling_period=sample_period, t_start=start_time,
  111. name=channel_names[chan_idx], channel_index=chan_idx)
  112. seg.analogsignals.append(analog)
  113. blk.segments.append(seg)
  114. blk.create_many_to_one_relationship()
  115. return blk