Соедините строки в фрейме данных Pandas, если выполнено условие

#python #pandas

Вопрос:

Допустим, у меня есть фрейм данных, подобный следующему:

 df = pd.DataFrame({
    'group':['A', 'A', 'A', 'B', 'B', 'C'], 
    'amount':[100, -100, 50, 30, -30, 40]
})
 

Если я хотел бы добавить еще один столбец, чтобы проверить, можно ли сопоставить количество каждой строки (т. Е. Одинаковое количество, но 1 положительное и 1 отрицательное) в группе.

Например, в группе A 100 и -100 могут быть сопряжены, тогда они будут истинными, в то время как 50 не могут найти пару, тогда это Ложь (как в следующей таблице).

Группа сумма пара
A 100 Правда
A -100 Правда
A 50 Ложный
B 30 Правда
B -30 Правда
C 40 Ложный

Каков был бы наиболее эффективный способ сделать это?

Ответ №1:

Мы можем взять abs amount столбец, а затем создать pair столбец на основе того, где находятся значения DataFrame.duplicated :

 df['pair'] = df.assign(amount=df['amount'].abs()).duplicated(keep=False)
 

* keep=False означает, что обе дублированные строки получают True . Правая сторона также может быть subset , если фрейм данных содержит более этих 2 столбцов.

df :

   group  amount   pair
0     A     100   True
1     A    -100   True
2     A      50  False
3     B      30   True
4     B     -30   True
5     C      40  False
 

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

Обновленный фрейм данных:

 df = pd.DataFrame({
    'group': ['A', 'A', 'A', 'A', 'B', 'B', 'C'],
    'amount': [100, -100, 50, 50, 30, -30, 40]
})
 

Развернитесь к широкой форме и проверьте наличие пар:

 df['abs_amount'] = df['amount'].abs()
df = df.join(
    df.pivot_table(index=['group', 'abs_amount'],
                   columns=df['amount'].gt(0),
                   values='amount',
                   aggfunc='first')
        .notnull().all(axis=1)
        .rename('pair'),
    on=['group', 'abs_amount']
).drop('abs_amount', axis=1)
 

df :

   group  amount   pair
0     A     100   True
1     A    -100   True
2     A      50  False
3     A      50  False
4     B      30   True
5     B     -30   True
6     C      40  False
 

То pivot_table :

 df['abs_amount'] = df['amount'].abs()
df.pivot_table(index=['group', 'abs_amount'],
               columns=df['amount'].gt(0),
               values='amount',
               aggfunc='first')
 
 amount            False   True
group abs_amount              
A     50            NaN   50.0  # Multiple 50s but no -50
      100        -100.0  100.0
B     30          -30.0   30.0
C     40            NaN   40.0
 

Убедитесь, что все значения в строке:

 df.pivot_table(index=['group', 'abs_amount'],
               columns=df['amount'].gt(0),
               values='amount',
               aggfunc='first').notnull().all(axis=1)
 
 group  abs_amount
A      50            False
       100            True
B      30             True
C      40            False
dtype: bool
 

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

1. Небольшое улучшение df.назначить(пара = df[‘сумма’].abs().дублировано(сохранить=Ложь))

2. Но что, если бы у C была сумма 100? Тогда C (idx 5) pair будет отмечен True , даже если в C. @BENY нет соответствующего значения

3. Мне просто интересно, есть ли 3 100 в одной группе

4. Привет, @HenryEcker, я обнаружил, что, если, скажем, группа А имеет сумму в [100, -100, 50, 50], тогда этот подход также будет рассматривать два 50 как пару, есть ли способ исключить их?

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