'Generates a random batch for training or validation.
Structures each element of the batch as an \'episode\'.
Each episode contains episode_length examples and
episode_width distinct labels.
Args:
data: A dictionary mapping label to list of examples.
episode_length: Number of examples in each episode.
episode_width: Distinct number of labels in each episode.
batch_size: Batch size (number of episodes).
Returns:
A tuple (x, y) where x is a list of batches of examples
with size episode_length and y is a list of batches of labels.'

def sample_episode_batch(self, data, episode_length, episode_width, batch_size):

    episodes_x = [[] for _ in xrange(episode_length)]
    episodes_y = [[] for _ in xrange(episode_length)]
    assert (len(data) >= episode_width)
    keys = data.keys()
    for b in xrange(batch_size):
        episode_labels = random.sample(keys, episode_width)
        remainder = (episode_length % episode_width)
        remainders = (([0] * (episode_width - remainder)) + ([1] * remainder))
        episode_x = [random.sample(data[lab], (r + ((episode_length - remainder) / episode_width))) for (lab, r) in zip(episode_labels, remainders)]
        episode = sum([[(x, i, ii) for (ii, x) in enumerate(xx)] for (i, xx) in enumerate(episode_x)], [])
        random.shuffle(episode)
        episode.sort(key=(lambda elem: elem[2]))
        assert (len(episode) == episode_length)
        for i in xrange(episode_length):
            episodes_x[i].append(episode[i][0])
            episodes_y[i].append((episode[i][1] + (b * episode_width)))
    return ([np.array(xx).astype('float32') for xx in episodes_x], [np.array(yy).astype('int32') for yy in episodes_y])
