Для данного pandas кадра данных df
я бы хотел сравнить каждый образец (строку) друг с другом.
Для больших наборов данных это приведет к слишком большому количеству сравнений (n**2
). Следовательно, необходимо проводить эти сравнения только для небольших групп (т. Е. Для всех тех, которые имеют одинаковые id
) и максимально эффективно.
Я хотел бы построить фрейм данных (df_pairs
), который содержит в каждой строке одну пару. Кроме того, я хотел бы получить все парные индексы (в идеале в виде набора Python).
Сначала я создаю пример кадра данных:
import numpy as np
import pandas as pd
from functools import reduce
from itertools import product, combinations
n_samples = 10_000
suffixes = ["_1", "_2"] # for df_pairs
id_str = "id"
df = pd.DataFrame({id_str: np.random.randint(0, 10, n_samples),
"A": np.random.randint(0, 100, n_samples),
"B": np.random.randint(0, 100, n_samples),
"C": np.random.randint(0, 100, n_samples)}, index=range(0, n_samples))
columns_df_pairs = ([elem + suffixes[0] for elem in df.columns] +
[elem + suffixes[1] for elem in df.columns])
Далее я сравниваю 4 различных варианта с соответствующими показателями производительности:
Опция 1
groups = df.groupby(id_str).groups # get the groups
pairs_per_group = [set(product(elem.tolist(), repeat=2)) for _, elem in groups.items()] # determine pairs per group
set_of_pairs = reduce(set.union, pairs_per_group) # convert all groups into one set
idcs1, idcs2 = zip(*[(e1, e2) for e1, e2 in set_of_pairs])
df_pairs = pd.DataFrame(np.hstack([df.values[idcs1, :], df.values[idcs2, :]]), # construct the dataframe of pairs
columns=columns_df_pairs,
index=pd.MultiIndex.from_tuples(set_of_pairs, names=('index 1', 'index 2')))
df_pairs.drop([id_str + suffixes[0], id_str + suffixes[1]], inplace=True, axis=1)
Опция 1 занимает 34,2 с ± 1,28 с.
Опция 2
groups = df.groupby(id_str).groups # get the groups
pairs_per_group = [np.array(np.meshgrid(elem.values, elem.values)).T.reshape(-1, 2) for _, elem in groups.items()]
idcs = np.unique(np.vstack(pairs_per_group), axis=0)
df_pairs2 = pd.DataFrame(np.hstack([df.values[idcs[:, 0], :], df.values[idcs[:, 1], :]]), # construct the dataframe of pairs
columns=columns_df_pairs,
index=pd.MultiIndex.from_arrays([idcs[:, 0], idcs[:, 1]], names=('index 1', 'index 2')))
df_pairs2.drop([id_str + suffixes[0], id_str + suffixes[1]], inplace=True, axis=1)
Опция 2 занимает 13 с ± 1,34 с.
Опция 3
groups = df.groupby(id_str).groups # get the groups
pairs_per_group = [np.array([np.tile(elem.values, len(elem.values)), np.repeat(elem.values, len(elem.values))]).T.reshape(-1, 2) for _, elem in groups.items()]
idcs = np.unique(np.vstack(pairs_per_group), axis=0)
df_pairs3 = pd.DataFrame(np.hstack([df.values[idcs[:, 0], :], df.values[idcs[:, 1], :]]), # construct the dataframe of pairs
columns=columns_df_pairs,
index=pd.MultiIndex.from_arrays([idcs[:, 0], idcs[:, 1]], names=('index 1', 'index 2')))
df_pairs3.drop([id_str + suffixes[0], id_str + suffixes[1]], inplace=True, axis=1)
Опция 3 занимает 12,1 с ± 347 мс.
опция 4
df_pairs4 = pd.merge(left=df, right=df, how="inner", on=id_str, suffixes=suffixes)
# here, I do not know how to get the MultiIndex in
df_pairs4.drop([id_str], inplace=True, axis=1)
опция 4 вычисляется быстрее всего с 1,41 с ± 239 мс. Однако у меня нет парных индексов в этом случае.
Я мог бы немного улучшить производительность, используя comparisons
вместо product
itertools. Я мог бы также построить матрицу сравнения и использовать только верхнюю тройку angular и построить оттуда свой фрейм данных. Это, однако, не кажется более эффективным, чем выполнение декартового произведения и удаление собственных ссылок, а также обратных сравнений (a, b) = (b, a)
.
- Не могли бы вы сказать мне более эффективный способ получения пар для сравнения (в идеале, как набор, чтобы иметь возможность использовать операции над множествами)?
- Могу ли я использовать
merge
или другой pandas
функция для построения моего желаемого кадра данных с мультииндексом?