docs: def rtCalibration(fiContainer, allowedRtDev=60, allowedMzDev=2.5,
                    reference=None, specfiles=None, showPlots=False,
                    plotDir=None, minIntensity=1e5):
      """Performs a retention time calibration between :class:`FeatureItem` of multiple specfiles.
  
      :ivar fiContainer: Perform alignment on :class:`FeatureItem` in :attr:`FeatureContainer.specfiles`
      :ivar allowedRtDev: maxium retention time difference of two features in two runs to be matched
      :ivar allowedMzDev: maxium relative m/z difference (in ppm) of two features in two runs to be matched
      :ivar showPlots: boolean, True if a plot should be generated which shows to results of the calibration
      :ivar plotDir: if not None and showPlots is True, the plots are saved to
          this location.
      :ivar reference: Can be used to specifically specify a reference specfile
      :ivar specfiles: Limit alignment to those specfiles in the fiContainer
      :ivar minIntensity: consider only features with an intensity above this value
      """
      #TODO: long function, maybe split into subfunctions
      specfiles = [_ for _ in viewkeys(fiContainer.info)] if specfiles is None else specfiles
      matchCharge = True
  
      refMzKey = 'mz'
      mzKey = 'mz'
  
      if reference is not None:
          if reference in specfiles:
              specfiles = [reference] + list(set(specfiles).difference(set([reference])))
          else:
              print('Specified reference specfile not present, using reference: ', specfiles[0])
  
      for featureItem in fiContainer.getItems(specfiles=specfiles):
          if not hasattr(featureItem, 'obsRt'):
              setattr(featureItem, 'obsRt', featureItem.rt)
  
      referenceArrays = None
      for specfile in specfiles:
          featureArrays = fiContainer.getArrays(['rt', 'charge', 'mz', 'intensity'],
                                                specfiles=specfile, sort='rt'
                                                )
          if minIntensity is not None:
              intensityMask = (featureArrays['intensity'] > minIntensity)
              for key in list(viewkeys(featureArrays)):
                  featureArrays[key] = featureArrays[key][intensityMask]
  
          if referenceArrays is None:
              referenceArrays = featureArrays
              if showPlots:
                  print('Reference: '+specfile)
              continue
  
          rtPosList = list()
          rtDevList = list()
          mzDevRelList = list()
          mzDevAbsList = list()
  
          for featurePos in range(len(featureArrays[mzKey])):
              currRt = featureArrays['rt'][featurePos]
              currMz = featureArrays[mzKey][featurePos]
              currZ = featureArrays['charge'][featurePos]
              mzLimitUp = currMz*(1+allowedMzDev*1E-6)
              mzLimitLow = currMz*(1-allowedMzDev*1E-6)
              rtLimitUp = currRt+allowedRtDev
              rtLimitLow = currRt-allowedRtDev
  
              posL = bisect.bisect_left(referenceArrays['rt'], rtLimitLow)
              posU = bisect.bisect_right(referenceArrays['rt'], rtLimitUp)
  
              refMask = (referenceArrays[refMzKey][posL:posU] <= mzLimitUp) & (referenceArrays[refMzKey][posL:posU] >= mzLimitLow)
              if matchCharge:
                  refMask = refMask & (referenceArrays['charge'][posL:posU] == currZ)
  
              currMzDev = abs(referenceArrays[refMzKey][posL:posU][refMask] - currMz)
              bestHitMask = currMzDev.argsort()
              for refRt, refMz in zip(referenceArrays['rt'][posL:posU][refMask][bestHitMask],
                                      referenceArrays[refMzKey][posL:posU][refMask][bestHitMask]):
                  rtPosList.append(currRt)
                  rtDevList.append(currRt - refRt)
                  mzDevRelList.append((1 - currMz / refMz)*1E6)
                  mzDevAbsList.append(currMz - refMz)
                  break
  
          rtPosList = numpy.array(rtPosList)
          rtDevList = numpy.array(rtDevList)
  
          splineInitialKnots = int(max(rtPosList) - min(rtPosList))
          dataFit = aux.DataFit(rtDevList, rtPosList)
          dataFit.splineInitialKnots = splineInitialKnots
          dataFit.splineTerminalExpansion = 0.2
          dataFit.processInput(dataAveraging='median', windowSize=10)
          dataFit.generateSplines()
  
          if showPlots:
              corrDevArr = rtDevList - dataFit.corrArray(rtPosList)
              timePoints = [min(rtPosList) + x for x in range(int(max(rtPosList)-min(rtPosList)))]
              corrValues  = dataFit.corrArray(timePoints)
              fig, ax = plt.subplots(3, 2, sharex=False, sharey=False, figsize=(20, 18))
              fig.suptitle(specfile)
              ax[0][0].hist(rtDevList, bins=100, color='grey', alpha=0.5, label='observed')
              ax[0][0].hist(corrDevArr, bins=100, color='red', alpha=0.5, label='corrected')
              ax[0][0].set_title('Retention time deviation')
              ax[0][0].legend()
              ax[0][0].set_xlim(allowedRtDev*-1, allowedRtDev)
              ax[0][1].hist(mzDevRelList, bins=100, color='grey')
              ax[0][1].set_title('Mz deviation [ppm]')
              ax[1][0].scatter(rtPosList, rtDevList, color='grey', alpha=0.1, label='observed')
              ax[1][0].plot(timePoints,corrValues, color='red', alpha=0.5, label='correction function')
              ax[1][0].set_title('Retention time deviation over time')
              ax[1][0].legend()
              ax[1][0].set_ylim(allowedRtDev*-1, allowedRtDev)
              ax[1][1].scatter(rtPosList, mzDevRelList, color='grey', alpha=0.1)
              ax[1][1].set_title('Mz deviation over time')
              ax[1][1].set_ylim(allowedMzDev*-1, allowedMzDev)
              ax[2][0].scatter(rtPosList, corrDevArr, color='grey', alpha=0.1)
              ax[2][0].set_title('Aligned retention time deviation over time')
              ax[2][0].set_ylim(allowedRtDev*-1, allowedRtDev)
              if plotDir is not None:
                  plotloc = aux.joinpath(plotDir, specfile+'.rtAlign.png')
                  fig.savefig(plotloc)
              else:
                  fig.show()
  
          featureArrays = fiContainer.getArrays(['rt'], specfiles=specfile, sort='rt')
          featureArrays['corrRt'] = featureArrays['rt'] - dataFit.corrArray(featureArrays['rt'])
          for featureId, corrRt, rt in zip(featureArrays['id'], featureArrays['corrRt'], featureArrays['rt']):
              fiContainer.container[specfile][featureId].rt = corrRt