title: Generate triplets from Pandas dataframe
body: <p>I am trying to generate all triplets of data from a Pandas dataframe based on a class or label. Say I have a dataframe with a unique identifier for each row, and a class/label for each row. I want triplets where the first two elements are from the same class/label, and the last element is from a different class/label. I am trying to get <em>all</em> such triplets.</p>
  
  <p>I can generate the combinations of elements with the <em>same</em> label just fine, but when I try to extend these with the elements that have <em>different</em> labels, I get an array filled with <code>None</code>.</p>
  
  <p>My example dataframe:</p>
  
  <pre><code>import pandas as pd
  import numpy as np
  
  df = pd.DataFrame({'uuid': np.arange(5),
                     'label': [0, 1, 1, 0, 0]})
  print(df)
  
     label  uuid
  0      0     0
  1      1     1
  2      1     2
  3      0     3
  4      0     4
  </code></pre>
  
  <p>Note the <code>uuid</code> column is just a placeholder here. Point is that it's unique for each row. The following generates all combinations of <em>same</em> elements and plugs them into a list:</p>
  
  <pre><code>import itertools as it
  
  labels = df.label.unique()
  all_combos = []
  for l in labels:
      combos = list(it.combinations(df.loc[df.label == l].as_matrix(), 2))
      all_combos.extend([list(c) for c in combos])  # convert to list because I anticipate needing to add to each combo later
  all_combos
  
  [[array([0, 0]), array([0, 3])],
   [array([0, 0]), array([0, 4])],
   [array([0, 3]), array([0, 4])],
   [array([1, 1]), array([1, 2])]]
  </code></pre>
  
  <p>Now I want all of these combinations with each <em>different</em> element appended.</p>
  
  <h2>I try:</h2>
  
  <pre><code>for l in labels:
      combos = list(it.combinations(df.loc[df.label == l].as_matrix(), 2))
      combo_list = [list(c) for c in combos]
      for c in combo_list:
          new_combos = [list(c).extend(s) for s in df.loc[df.label != l].as_matrix()]
          all_combos.append(new_combos)
  </code></pre>
  
  <h2>I expect:</h2>
  
  <pre><code>all_combos
  
  [[array([0, 0]), array([0, 3]), array([1, 1])],
   [array([0, 0]), array([0, 3]), array([1, 2])],
   [array([0, 0]), array([0, 4]), array([1, 1])],
   [array([0, 0]), array([0, 4]), array([1, 2])],
   [array([0, 3]), array([0, 4]), array([1, 1])],
   [array([0, 3]), array([0, 4]), array([1, 2])],
   [array([1, 1]), array([1, 2]), array([0, 0])],
   [array([1, 1]), array([1, 2]), array([0, 3])],
   [array([1, 1]), array([1, 2]), array([0, 4])]]
  </code></pre>
  
  <h2>I get:</h2>
  
  <pre><code>all_combos
  
  [[None, None], [None, None], [None, None], [None, None, None]]
  </code></pre>
  
  <p>which seems really odd: they aren't even all the same length! But I do have the same number of <code>None</code>'s in my result as the expected number of valid triplets.</p>
  
  <p>I also tried <code>all_combos.extend(new_combos)</code> and got a 1-D list of 9 elements, so just a flattened version of the result above. In fact any combination of <code>list.extend</code> and <code>list.append</code> in the last two lines of the inner loop give me either the result shown above, or a flattened version of that result, neither of which makes sense to me.</p>
  
  <p><strong>Edit:</strong> as mentioned in the comments, <code>list.extend</code> and <code>list.append</code> are in-place operations, so they wouldn't return anything. How then do I get my list comprehension to give me these values? Or refactor to something else that works?</p>