Необычные значения из двух фреймов данных по строкам в python

#python #pandas #dataframe

#python #pandas #фрейм данных

Вопрос:

У меня есть два df1 фрейма данных и df2 . Первый столбец в обоих — это идентификатор клиента, который является an int , но другие столбцы содержат различные строковые значения. Я хочу создать новый df3 фрейм данных, который содержит для каждого идентификатора клиента набор значений, найденных в df2 , но не в df1 .

Пример:

df1 :

      v1 v2 v3 v4
cust            
1     A  B  B  A
2     A  A  A  A
3     B  B  A  A
4     B  C  A  A
 

df2 :

      v1 v2 v3 v4
cust            
1     A  A  C  B
2     A  A  C  B
3     C  B  B  A
4     C  B  B  A
 

Ожидаемый результат:

 cust
1       {C}
2    {B, C}
3       {C}
4        {}
 

Комментарии:

1. Пожалуйста, не добавляйте изображения, добавляйте фреймы данных таким образом, чтобы их можно было скопировать в скрипт Python.

2. @DaniMesejo Я здесь новичок. Я пробовал другой формат, но он не работает. Надеюсь, я получу от вас помощь.

Ответ №1:

 In [2]: df_2 = pd.DataFrame({"KundelID" : list(range(1,11)),
   ...:               'V1' : list('AACCBBBCCC'),
   ...:               'V2' : list('AABBBCCCAA'),
   ...:               'V3' : list('CCBBBBBAAB'),
   ...:               'V4' : list('BBAACAAAAB')})
   ...: df_1 = pd.DataFrame({"KundelID" : list(range(1,11)),
   ...:               'V1' : list('AABBCCCCCC'),
   ...:               'V2' : list('BABCCCCAAA'),
   ...:               'V3' : list('BAAAAABBBB'),
   ...:               'V4' : list('AAAACCCCBB')})

In [3]: df_1
Out[3]: 
   KundelID V1 V2 V3 V4
0         1  A  B  B  A
1         2  A  A  A  A
2         3  B  B  A  A
3         4  B  C  A  A
4         5  C  C  A  C
5         6  C  C  A  C
6         7  C  C  B  C
7         8  C  A  B  C
8         9  C  A  B  B
9        10  C  A  B  B

In [4]: df_2
Out[4]: 
   KundelID V1 V2 V3 V4
0         1  A  A  C  B
1         2  A  A  C  B
2         3  C  B  B  A
3         4  C  B  B  A
4         5  B  B  B  C
5         6  B  C  B  A
6         7  B  C  B  A
7         8  C  C  A  A
8         9  C  A  A  A
9        10  C  A  B  B

In [7]: pd.DataFrame({"KundeID" : df_2.KundelID,
   ...:             'Not-in-df_1' : [','.join([i for i in df_2_ if not i in df_1_]) if [i for i in df_2_ if not i in df_1_] else None for df_1_,df_2_ in zip(df_1.T[1:].apply(np.unique), df_2.T[1:].apply(np.unique))]})
Out[7]: 
   KundeID Not-in-df_1
0        1           C
1        2         B,C
2        3           C
3        4        None
4        5           B
5        6           B
6        7           A
7        8        None
8        9        None
9       10        None


 

Комментарии:

1. Хотя (судя по выводам) это обеспечивает и ответ, вы можете сделать свой ответ более ценным, предоставив объяснение того, как вы достигли результата. Возможно, разделение (и объяснение) длинной строки кода Python может помочь?

Ответ №2:

Идея состоит в том, чтобы преобразовать все значения в каждой строке в set . Затем мы можем взять установленную разницу для каждого идентификатора клиента. Это позволяет избежать циклов и понимания списка:

 df3 = (
    pd
    .concat([
        df1.reindex(index=df2.index).apply(set, axis=1),
        df2.apply(set, axis=1),
    ], axis=1)
    .apply(lambda r: r[1].difference(r[0]), axis=1)
)
print(df3)
# Out:
cust
1       {C}
2    {B, C}
3       {C}
4        {}
 

Примечания:

  1. Бит df1.reindex(index=df2.index) используется в случае, если некоторые идентификаторы отсутствуют в df1 or df2 ).
  2. Тривиально преобразовать выходные данные во что-то другое вместо a set . Например ','.join(r[1].difference(r[0])) , как лямбда-выражение будет создавать строки.

Настройка:

Для дальнейшего использования, чтобы упростить воспроизводимый пример, было бы неплохо предоставить некоторый код, который может быть скопирован / вставлен непосредственно пользователями для быстрого запуска вашей проблемы.

 df1 = pd.read_csv(io.StringIO("""
1 A B B A
2 A A A A
3 B B A A
4 B C A A
"""), sep=' ', names='cust v1 v2 v3 v4'.split()).set_index('cust')

df2 = pd.read_csv(io.StringIO("""
1 A A C B
2 A A C B
3 C B B A
4 C B B A
"""), sep=' ', names='cust v1 v2 v3 v4'.split()).set_index('cust')
 

Ответ №3:

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

 df2.apply(set, axis=1) - df1.apply(set, axis=1)
 

Вывод:

 cust
1       {C}
2    {C, B}
3       {C}
4        {}
dtype: object
 

Если вам нужна симметричная разница между наборами данных (т.Е. Элементами либо в наборе, либо в другом, но не в обоих), тогда лучше использовать pd.concat :

 dfs = [df1, df2]
pd.concat([df.apply(set, 1) for df in dfs], 1).apply(lambda x: x[0]^x[1], 1)
 

где 1 здесь означает axis=1 . Кроме того, замена x[0]^x[1] на set.symmetric_difference(*x) также должна работать.

Интересно, Series_A ^ Series_B что работает не так, как ожидалось, вместо этого (по-видимому) он возвращает серию bool, сообщающую нам, не являются ли возвращаемые значения из операций set пустыми.