docs: def read_data(self, file_handle):
          """Read the DATA segment of the FCS file."""
          self._verify_assumptions()
          text = self.annotation
  
          if (self._data_start > self._file_size) or (self._data_end > self._file_size):
              raise ValueError(u'The FCS file "{}" is corrupted. Part of the data segment '
                               u'is missing.'.format(self.path))
  
          num_events = text['$TOT']  # Number of events recorded
          num_pars = text['$PAR']  # Number of parameters recorded
  
          if text['$BYTEORD'].strip() == '1,2,3,4' or text['$BYTEORD'].strip() == '1,2':
              endian = '<'
          elif text['$BYTEORD'].strip() == '4,3,2,1' or text['$BYTEORD'].strip() == '2,1':
              endian = '>'
          else:
              msg = 'Unrecognized byte order ({})'.format(text['$BYTEORD'])
              raise ParserFeatureNotImplementedError(msg)
  
          # dictionary to convert from FCS format to numpy convention
          conversion_dict = {'F': 'f', 'D': 'f', 'I': 'u'}
  
          if text['$DATATYPE'] not in conversion_dict.keys():
              raise ParserFeatureNotImplementedError('$DATATYPE = {0} is not yet '
                                                     'supported.'.format(text['$DATATYPE']))
  
          # Calculations to figure out data types of each of parameters
          # $PnB specifies the number of bits reserved for a measurement of parameter n
          bytes_per_par_list = [int(text['$P{0}B'.format(i)] / 8) for i in self.channel_numbers]
  
          par_numeric_type_list = [
              '{endian}{type}{size}'.format(endian=endian,
                                            type=conversion_dict[text['$DATATYPE']],
                                            size=bytes_per_par)
              for bytes_per_par in bytes_per_par_list
          ]
  
          # Parser for list mode. Here, the order is a list of tuples.
          # Each tuple stores event related information
          file_handle.seek(self._data_start, 0)  # Go to the part of the file where data starts
  
          ##
          # Read in the data
          if len(set(par_numeric_type_list)) > 1:
              # This branch deals with files in which the different columns (channels)
              # were encoded with different types; i.e., a mixed data format.
              dtype = ','.join(par_numeric_type_list)
              data = fromfile(file_handle, dtype, num_events)
  
              # The dtypes in the numpy array `data` above are associated with both a name
              # and a type; i.e.,
              # https://docs.scipy.org/doc/numpy/reference/generated/numpy.recarray.html
              # The names are assigned automatically.
              # In order for this code to work correctly with the pandas DataFrame constructor,
              # we convert the *names* of the dtypes to the channel names we want to use.
  
              names = self.get_channel_names()
  
              if six.PY2:
                  encoded_names = [name.encode('ascii', errors='replace') for name in names]
              else:  # Assume that python3 or older then.
                  encoded_names = [name for name in names]
  
              data.dtype.names = tuple(encoded_names)
          else:
              # values saved in a single data format
              dtype = par_numeric_type_list[0]
              data = fromfile(file_handle, dtype, num_events * num_pars)
              data = data.reshape((num_events, num_pars))
          ##
          # Convert to native byte order
          # This is needed for working with pandas data structures
          native_code = '<' if (sys.byteorder == 'little') else '>'
          if endian != native_code:
              # swaps the actual bytes and also the endianness
              data = data.byteswap().newbyteorder()
  
          self._data = data