использование isin в нескольких столбцах

Я пытаюсь использовать .isin с ~, чтобы получить список уникальных строк на основе нескольких столбцов в двух наборах данных.

Итак, у меня есть 2 набора данных с 9 строками: df1 — нижний, а df2 — верхний (извините, но я не смог показать оба ниже, он показал 1, а затем ряд чисел)

   Index    Serial  Count   Churn
     1       9         5    0
     2       8         6    0
     3       10        2    1
     4       7         4    2
     5       7         9    2
     6       10        2    2
     7       2         9    1
     8       9         8    3
     9       4         3    5


    Index   Serial  Count   Churn
     1       10      2       1
     2       10      2       1
     3       9       3       0
     4       8       6       0
     5       9       8       0
     6       1       9       1
     7       10      3       1
     8       6       7       1
     9       4       8       0

Я хотел бы получить список строк из df1, которых нет в df2, на основе более чем 1 столбца.

Например, если я основываю свой поиск на столбцах Serial и Count, я не получу индекс 1 и 2 обратно из df1, поскольку он отображается в df2 в позиции индекса 6, то же самое с позицией индекса 4 в df1, поскольку он отображается в позиции индекса 2. в дф2. То же самое относится к позиции индекса 5 в df1, как и к позиции индекса 8 в df2.

Колонка оттока не имеет большого значения.

Я могу заставить его работать, но только на основе 1 столбца, но не более 1 столбца.

df2[~df2.Serial.isin(df1.Serial.values)] делает то, что я хочу, но только в 1 столбце. Я хочу, чтобы он был основан на 2 или более.

  Index Serial  Count   Churn
   3    9          3    0
   6    1          9    1
   7    10         3    1
   8    6          7    1
   9    4          8    0

person verdi    schedule 31.07.2019    source источник
comment
Я немного запутался, вам нужны строки из df1, которых нет в df2, но ваш код предполагает обратное, строки в df2, которых нет в df1.   -  person Mattias    schedule 31.07.2019
comment
Извините, я никогда не замечал. TBH это может быть в любом случае, поскольку это просто означает переключение df1 и df2. Это скорее то, как я заставляю запрос работать, который я ищу.   -  person verdi    schedule 31.07.2019
comment
Это правда, я считаю, что ваше редактирование испортило один из ваших фреймов данных, поскольку теперь они содержат одни и те же значения.   -  person Mattias    schedule 31.07.2019
comment
Да, извините, это было, и я никогда не замечал этого. Я исправил это сейчас.   -  person verdi    schedule 31.07.2019


Ответы (2)


Одним из решений является слияние с индикаторами:

df1 = pd.DataFrame([[10, 2, 0], [9, 4, 1], [9, 8, 1], [8, 6, 1], [9, 8, 1], [1, 9, 1], [10, 3, 1], [6, 7, 1], [4, 8, 1]], columns=['Serial', 'Count', 'Churn'])
df2 = pd.DataFrame([[9, 5, 1], [8, 6, 1], [10, 2, 1], [7, 4, 1], [7, 9, 1], [10, 2, 1], [2, 9, 1], [9, 8, 1], [4, 3, 1]], columns=['Serial', 'Count', 'Churn'])
# merge with indicator on
df_temp = df1.merge(df2[['Serial', 'Count']].drop_duplicates(), on=['Serial', 'Count'], how='left', indicator=True)
res = df_temp.loc[df_temp['_merge'] == 'left_only'].drop('_merge', axis=1)

Output        
   Serial  Count  Churn
1       9      4      1
5       1      9      1
6      10      3      1
7       6      7      1
8       4      8      1
person Mattias    schedule 31.07.2019
comment
Спасибо, это сработало. Я не могу дать вам полезный ответ, так как у меня недостаточно представителей. - person verdi; 31.07.2019
comment
Готово, спасибо. Первый пост здесь, поэтому я даже не знал, что это вариант. спасибо еще раз. - person verdi; 01.08.2019
comment
Нет проблем, рад, что смог помочь! - person Mattias; 01.08.2019

У меня была похожая проблема для решения, я нашел самый простой способ справиться с ней, создав временный столбец, который состоит из объединенных столбцов идентификаторов и используя isin для этих вновь созданных значений временного столбца.

Простая функция, достигающая этого, может быть следующей

from functools import reduce

get_temp_col = lambda df, cols: reduce(lambda x, y: x + df[y].astype('str'), cols, "")

def subset_on_x_columns(df1, df2, cols):
    """
    Subsets the input dataframe `df1` based on the missing unique values of input columns
    `cols` of dataframe `df2`.

    :param df1: Pandas dataframe to be subsetted
    :param df2: Pandas dataframe which missing values are going to be 
                used to subset `df1` by
    :param cols: List of column names
    """
    df1_temp_col = get_temp_col(df1, cols)
    df2_temp_col = get_temp_col(df2, cols)

    return df1[~df1_temp_col.isin(df2_temp_col.unique())]

Таким образом, для вашего случая все, что нужно, это выполнить:

result_df = subset_on_x_columns(df1, df2, ['Serial', 'Count'])

который имеет нужные строки:

   Index  Serial  Count  Churn
      3       9      3      0
      6       1      9      1
      7      10      3      1
      8       6      7      1
      9       4      8      0

Преимущество этого решения в том, что оно естественным образом масштабируется по количеству используемых столбцов, т.е. все, что нужно, это указать в списке входных параметров cols, какие столбцы использовать в качестве идентификаторов.

person Richard Nemeth    schedule 31.07.2019