title: Correlation of 2 time dependent multidimensional signals (signal vectors)
body: <p>I have a matrix M1 , each row of which is a time-dependent signal.</p>
  
  <p>And I have another matrix, M2,  of the same dimensions, each row of which is also a time dependent signal, used as a "template", to recognize signalshapes in the first matrix.</p>
  
  <p>I want as a result a column vector v, with v [i] is the correllation between the i'th row of M1 and the i'th row of M2.</p>
  
  <p>I've looked into the corrcoef function of numpy and tried the following code:</p>
  
  <pre><code>import numpy as np
  
  M1 = np.array ([
      [1, 2, 3, 4],
      [2, 3, 1, 4]
  ])
  
  M2 = np.array ([
      [10, 20, 30, 40],
      [20, 30, 10, 40]
  ])
  
  print (np.corrcoef (M1, M2))
  </code></pre>
  
  <p>which prints:</p>
  
  <pre><code>[[ 1.   0.4  1.   0.4]
   [ 0.4  1.   0.4  1. ]
   [ 1.   0.4  1.   0.4]
   [ 0.4  1.   0.4  1. ]]
  </code></pre>
  
  <p>I've been reading the docs, but I am still confused as to which entries of this matrix I have to pick as entries of my vector v.</p>
  
  <p>Can anyone help?</p>
  
  <p>(I've studied several S.O. answers to similar questions, but didn't yet see the light...)</p>
  
  <p>Code context:</p>
  
  <p>There are 256 rows (signals), and I run a sliding window of 200 samples over the 'main signal', which has a lenght of 10k samples. So M1 and M2 are both 256 rows x 200 columns. Sorry for the erroneous 10k samples. That's the total signal length. By using correlation with a sliding template I try to find the offsets where the template matches best. Actually I am looking for QRS complexes in a 256 channel invasive cardiogram (or rather, electrogram, as physicians call it). </p>
  
  <pre><code>    lg.info ('Processor: {}, time: {}, markers: {}'.format (self.key, dt.datetime.now ().time (), len (self.data.markers)))
  
      # Compute average signal shape over preexisting markers and uses that as a template to find the others.
      # All generated markers will have the width of the widest preexisting one.
  
      template = np.zeros ((self.data.samples.shape [0], self.bufferWidthSteps))
  
      # Add intervals that were marked in advance
      nrOfTerms = 0
      maxWidthSteps = 0
      newMarkers = []
      for marker in self.data.markers:
          if marker.key == self.markerKey:
  
              # Find start and stop sample index    
              startIndex = marker.tSteps - marker.stampWidthSteps // 2
              stopIndex = marker.tSteps + marker.stampWidthSteps // 2
  
              # Extract relevant slice from samples and add it to template
              template += np.hstack ((self.data.samples [ : , startIndex : stopIndex], np.zeros ((self.data.samples.shape [0], self.bufferWidthSteps - marker.stampWidthSteps))))
  
              # Adapt nr of added terms to facilitate averaging
              nrOfTerms += 1
  
              # Remember maximum width of previously marked QRS complexes
              maxWidthSteps = max (maxWidthSteps, marker.stampWidthSteps)
          else:
              # Preexisting markers with non-matching keys are just copied to the new marker list
              # Preexisting markers with a matching key are omitted from the new marker list
              newMarkers.append (marker)
  
      # Compute average of intervals that were marked in advance
      template = template [ : , 0 : maxWidthSteps] / nrOfTerms
      halfWidthSteps = maxWidthSteps // 2
  
      # Append markers of intervals that yield an above threshold correlation with the averaged marked intervals
      firstIndex = 0
      stopIndex = self.data.samples.shape [1] - maxWidthSteps
      while firstIndex &lt; stopIndex:
          corr = np.corrcoef (
              template,
              self.data.samples [ : , firstIndex : firstIndex + maxWidthSteps]
          )
  
          diag = np.diagonal (
              corr,
              template.shape [0]
          )
  
          meanCorr = np.mean (diag)
  
          if meanCorr &gt; self.correlationThreshold:
              newMarkers.append ([self.markerFactories [self.markerKey] .make (firstIndex + halfWidthSteps, maxWidthSteps)])
  
              # Prevent overlapping markers
              firstIndex += maxWidthSteps
          else:
              firstIndex += 5
  
      self.data.markers = newMarkers
  
      lg.info ('Processor: {}, time: {}, markers: {}'.format (self.key, dt.datetime.now ().time (), len (self.data.markers)))
  </code></pre>