Панды создают флаг, если какое-либо условие не выполняется

#python #pandas

Вопрос:

У меня есть огромный фрейм данных pandas (на самом деле имеет 5 строк):

 df = pd.DataFrame({'id': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
                   'opn1': [20180301, 20180401, 20180501, 20180601, 20180701, 20180801, 20180901, 20181001, 20181101, 20181201],
                   'opn2': [20180401, 20180501, 20180601, 20180701, 20180801, 20180901, 20181001, 20181101, 20181201, 20190101],
                   'opn3': [20180501, 20180601, 20180701, 20180801, 20180901, 20181001, 20181101, 20181201, 20190101, 20190201],
                   'opn4': [20180601, 20180701, 20180801, 20180901, 20181001, 20181101, 20181201, 20190101, 20190201, 20190301],
                   'opn5': [20180701, 20180801, 20180901, 20181001, 20181101, 20181201, 20190101, 20190201, 20190301, 20190401],
                   'cls1': [0, 20180520, 0, 0, 0, 0, 0, 0, 0, 0],
                   'cls2': [0, 0, 0, 0, 20181031, 0, 0, 0, 0, 0],
                   'cls3': [0, 0, 20180725, 0, 20180701, 0, 0, 0, 0, 0],
                   'cls4': [0, 0, 0, 0, 0, 0, 20190101, 0, 0, 0],
                   'cls5': [0, 20180731, 0, 0, 0, 0, 0, 0, 0, 20190510],
                   })
 

Мое требование состоит в том, чтобы создать флаг со значением 1, если какая-либо из дат закрытия cls1..5 opn1..5

Пример вывода: для id==2, cls5

Я хочу избежать цикла и запустить его как можно быстрее. В моих данных ~5 млн строк.

Возможно np.where , с помощью комбинации any ?

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

1. вы можете добавить свой образец вывода?

2. идентификаторы 2 и 5 должны быть помечены как 1

3. Использование 0 в качестве отсутствующего значения кажется хуже, чем использование NaN. (это экономит память, но будет работать медленнее).

4. в моих данных уже есть нули.

5. Проведите сравнительный анализ производительности, чтобы сравнить решения. Мои тесты с использованием %%времени показывают, что мое решение является самым быстрым среди 3 решений, использующих данные выборки (мое решение 0,9 мс, в то время как другие решения составляют 1,4 мс, 3.x мс). Сейчас немного занят, поэтому не могу показать вам результаты сравнительного анализа.

Ответ №1:

Вы можете попробовать использовать np.any с axis=0 . Сначала создайте условие, а затем примените его к соответствующим столбцам:

 cond = lambda i: ((df['cls'   str(i)] != 0) amp; (df['cls'   str(i)] < df['opn'   str(i)])).values
df['flag'] = np.any([cond(i) for i in range(1,6)], axis=0).astype(int)
 

Это пометит строки с id=2 и id=5 .

Ответ №2:

Вы можете использовать .filter для фильтрации столбцов 2 части для сравнения. Затем используйте any дальше axis=1 , как показано ниже:

 df['flag'] = (  ((df.filter(like='cls').values != 0) amp; 
                 (df.filter(like='cls').values <  df.filter(like='opn').values)
                )
                .any(axis=1)
                .astype(int)
             )
 

Результат:

id 2 и 5 помечены.

 print(df)

   id      opn1      opn2      opn3      opn4      opn5      cls1      cls2      cls3      cls4      cls5  flag
0   1  20180301  20180401  20180501  20180601  20180701         0         0         0         0         0     0
1   2  20180401  20180501  20180601  20180701  20180801  20180520         0         0         0  20180731     1
2   3  20180501  20180601  20180701  20180801  20180901         0         0  20180725         0         0     0
3   4  20180601  20180701  20180801  20180901  20181001         0         0         0         0         0     0
4   5  20180701  20180801  20180901  20181001  20181101         0  20181031  20180701         0         0     1
5   6  20180801  20180901  20181001  20181101  20181201         0         0         0         0         0     0
6   7  20180901  20181001  20181101  20181201  20190101         0         0         0  20190101         0     0
7   8  20181001  20181101  20181201  20190101  20190201         0         0         0         0         0     0
8   9  20181101  20181201  20190101  20190201  20190301         0         0         0         0         0     0
9  10  20181201  20190101  20190201  20190301  20190401         0         0         0         0  20190510     0

 

Ответ №3:

вот еще одна версия, в которой вы перебираете возможные комбинации открытия и закрытия:

 df['flag'] = False
for i in range(1,6):
    df['flag'] = df['flag'] | (df[f'cls{i}'] < df[f'opn{i}']) amp; (df[f'cls{i}'] != 0)