Как объединить строки с комбинацией значений в DataFrame - PullRequest
0 голосов
/ 17 февраля 2019

У меня есть DataFrame (df1), как указано ниже

    Hair  Feathers  Legs  Type  Count
 R1  1       NaN     0     1      1
 R2  1        0      Nan   1      32
 R3  1        0      2     1      4
 R4  1       Nan     4     1      27

Я хочу объединить строки на основе различных комбинаций значений в каждом столбце, а также хочу добавить значения счетчиков для каждой объединенной строки.Результирующий кадр данных (df2) будет выглядеть следующим образом:

    Hair  Feathers  Legs  Type  Count
 R1   1      0        0     1     33
 R2   1      0        2     1     36
 R3   1      0        4     1     59

Объединение выполняется таким образом, что любое значение Nan будет объединено с 0 или 1. В df2 R1 рассчитывается путем объединениязначение Nan Feathers (df1, R1) со значением 0 Feathers (df1, R2).Аналогично, значение 0 в Legs (df1, R1) объединяется со значением Nan Legs (df1, R2).Затем добавляются числа R1 (1) и R2 (32).Таким же образом R2 и R3 объединяются, поскольку значение Feathers в R2 (df1) аналогично R3 (df1), а значение Legs Nan объединяется с 2 в R3 (df1) и счетом R2 (32) и R3(4) добавлены.

Надеюсь, объяснение имеет смысл.Любая помощь будет высоко оценена

1 Ответ

0 голосов
/ 17 февраля 2019

Возможный способ сделать это - реплицировать каждую из строк, содержащих NaN, и заполнить их значениями для столбца.

Во-первых, нам нужно получить возможные ненулевые уникальные значения для каждого столбца.:

unique_values = df.iloc[:, :-1].apply(
       lambda x: x.dropna().unique().tolist(), axis=0).to_dict()   
> unique_values
{'Hair': [1.0], 'Feathers': [0.0], 'Legs': [0.0, 2.0, 4.0], 'Type': [1.0]}

Затем выполните итерацию по каждой строке кадра данных и замените каждый NaN возможными значениями для каждого столбца.Мы можем сделать это, используя pandas.DataFrame.iterrows:

mask = df.iloc[:, :-1].isnull().any(axis=1)

# Keep the rows that do not contain `Nan`
# and then added modified rows

list_of_df = [r for i, r in df[~mask].iterrows()]

for row_index, row in df[mask].iterrows(): 

    for c in row[row.isnull()].index: 

        # For each column of the row, replace 
        # Nan by possible values for the column

        for v in unique_values[c]: 

            list_of_df.append(row.copy().fillna({c:v})) 

df_res = pd.concat(list_of_df, axis=1, ignore_index=True).T

В результате получается кадр данных, в котором все NaN заполнены возможными значениями для столбца:

> df_res

   Hair  Feathers  Legs  Type  Count
0   1.0       0.0   2.0   1.0    4.0
1   1.0       0.0   0.0   1.0    1.0
2   1.0       0.0   0.0   1.0   32.0
3   1.0       0.0   2.0   1.0   32.0
4   1.0       0.0   4.0   1.0   32.0
5   1.0       0.0   4.0   1.0   27.0

Чтобы получить окончательный результат группировки Count по возможным комбинациям ['Hair', 'Feathers', 'Legs', 'Type'], нам просто нужно сделать:

> df_res.groupby(['Hair', 'Feathers', 'Legs', 'Type']).sum().reset_index()  

   Hair  Feathers  Legs  Type  Count
0   1.0       0.0   0.0   1.0   33.0
1   1.0       0.0   2.0   1.0   36.0
2   1.0       0.0   4.0   1.0   59.0

Надеюсь, что это послужит

ОБНОВЛЕНИЕ

Если один или несколько элементов в строке отсутствуют, процедура ищет все возможные комбинации для отсутствующих значений одновременно.Давайте добавим новую строку с двумя пропущенными элементами:

> df

   Hair  Feathers  Legs  Type  Count
0   1.0       NaN   0.0   1.0    1.0
1   1.0       0.0   NaN   1.0   32.0
2   1.0       0.0   2.0   1.0    4.0
3   1.0       NaN   4.0   1.0   27.0
4   1.0       NaN   NaN   1.0   32.0

Мы будем действовать аналогичным образом, но комбинации замен будут получены с использованием itertools.product :

 import itertools 

 unique_values = df.iloc[:, :-1].apply(
       lambda x: x.dropna().unique().tolist(), axis=0).to_dict()

 mask = df.iloc[:, :-1].isnull().any(axis=1) 

 list_of_df = [r for i, r in df[~mask].iterrows()] 

 for row_index, row in df[mask].iterrows():  

     cols = row[row.isnull()].index.tolist() 

     for p in itertools.product(*[unique_values[c] for c in cols]): 

         list_of_df.append(row.copy().fillna({c:v for c, v in zip(cols, p)}))

 df_res = pd.concat(list_of_df, axis=1, ignore_index=True).T       


> df_res.sort_values(['Hair', 'Feathers', 'Legs', 'Type']).reset_index(drop=True)

Hair  Feathers  Legs  Type  Count
1   1.0       0.0   0.0   1.0    1.0
2   1.0       0.0   0.0   1.0   32.0
6   1.0       0.0   0.0   1.0   32.0
0   1.0       0.0   2.0   1.0    4.0
3   1.0       0.0   2.0   1.0   32.0
7   1.0       0.0   2.0   1.0   32.0
4   1.0       0.0   4.0   1.0   32.0
5   1.0       0.0   4.0   1.0   27.0
8   1.0       0.0   4.0   1.0   32.0

> df_res.groupby(['Hair', 'Feathers', 'Legs', 'Type']).sum().reset_index()

   Hair  Feathers  Legs  Type  Count
0   1.0       0.0   0.0   1.0   65.0
1   1.0       0.0   2.0   1.0   68.0
2   1.0       0.0   4.0   1.0   91.0
...