Как определить столбцы фрейма данных, содержащие определенный шаблон

#python #pandas

#python #панды

Вопрос:

Я хочу идентифицировать все столбцы, содержащие только определенный шаблон, который имеет шестнадцатеричный код длиной 32 символа. Итак, если у меня есть следующий фрейм данных, например

 Incident                                SLA Contributor                         Priority       Last Group
-----------------------------------------------------------------------------------------------------------------------------
0000329edb523708f8079044db9619f0        DAP/IBM                                    P2        3a03ba3a6f22f2c44484f5269f3ee425
3bb02e66db97a70cf8079044db961907        EUX/SD                                     P4        102a3748dbee2200d4c17868bf9619b1
3bb02e66db97a70cf8079044db961907        fa863d3fdb1d3740205a328c7c961982           P4        102a3748dbee2200d4c17868bf9619b1
82cf9812dbd80098b6629c4adb96198d        DAP/IBM                                    P4        3a03ba3a6f22f2c44484f5269f3ee425
9e12a6c11b228c14de583150cd4bcbbb        3a03ba3a6f22f2c44484f5269f3ee425           P4        64f4d44fdbf666004f9a7cbdae961919
b17c3f43db9bf700b6629c4adb9619ca        NET/BT                                     P3        64f4d44fdbf666004f9a7cbdae961919
fa863d3fdb1d3740205a328c7c961982        DAP/IBM                                    P2        3a03ba3a6f22f2c44484f5269f3ee425
 

Таким образом, он должен идентифицировать столбец Incident and Last Group , потому что все значения в этих двух столбцах представляют собой шестнадцатеричный код длиной 32 символа. Некоторые значения SLA Contributor столбца представляют собой шестнадцатеричный код длиной 32, но есть и другие значения, которые не соответствуют этому шаблону, поэтому он не должен включать SLA Contributor столбец.

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

Спасибо, Саттяки

Ответ №1:

Вы можете использовать all и регулярное выражение:

 hex_regex = re.compile(r"^[0-9a-fA-F]{32}$")  # regular expression of an hex code of 32 digits

hex_cols = [col for col in df.columns if df[col].map(lambda x: hex_regex.match(x) is not None).all()]  # check that all the entries in a column match the required format
 

Если разрешены nan, их следует пропустить, поэтому используйте следующее:

 hex_cols = [col for col in df.columns if df[df[col].notnull()][col].map(lambda x: hex_regex.match(x) is not None).all()]
 

В обоих случаях hex_cols это ['Incident', 'Last Group'] .

Вот полный пример (со значениями nan):

 import pandas as pd
import re

df = pd.DataFrame(
    data=[
        ['0000329edb523708f8079044db9619f0', 'DAP/IBM', 'P2', '3a03ba3a6f22f2c44484f5269f3ee425'],
        ['3bb02e66db97a70cf8079044db961907', 'EUX/SD', 'P4', '102a3748dbee2200d4c17868bf9619b1'],
        ['3bb02e66db97a70cf8079044db961907', 'fa863d3fdb1d3740205a328c7c961982', 'P4', '102a3748dbee2200d4c17868bf9619b1'],
        ['82cf9812dbd80098b6629c4adb96198d', 'DAP/IBM', 'P4', '3a03ba3a6f22f2c44484f5269f3ee425'],
        ['9e12a6c11b228c14de583150cd4bcbbb', '3a03ba3a6f22f2c44484f5269f3ee425', 'P4', '64f4d44fdbf666004f9a7cbdae961919'],
        ['b17c3f43db9bf700b6629c4adb9619ca', 'NET/BT', 'P3', '64f4d44fdbf666004f9a7cbdae961919'],
        ['fa863d3fdb1d3740205a328c7c961982', 'DAP/IBM', 'P2', '3a03ba3a6f22f2c44484f5269f3ee425'],
        [None, 'DAP/IBM', 'P2', '3a03ba3a6f22f2c44484f5269f3ee425'],
        ['b17c3f43db9bf700b6629c4adb9619ce', 'DAP/IBM', 'P2', None],
        [None, 'DAP/IBM', 'P2', None],
    ],
    columns=['Incident', 'SLA Contributor', 'Priority', 'Last Group'],
)

hex_regex = re.compile(r"^[0-9a-fA-F]{32}$")

hex_cols = [col for col in df.columns if df[df[col].notnull()][col].map(lambda x: hex_regex.match(x) is not None).all()]
 

Если вам нравится однострочный, вы можете сжать предыдущий код:

 [col for col in df.columns if df[df[col].notnull()][col].map(lambda x: re.compile(r"^[0-9a-fA-F]{32}$").match(x) is not None).all()]
 

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

1. Спасибо, ваш код работает как шарм! Я хотел бы принять это также как правильное.

Ответ №2:

Для более общего решения сначала выберите только строки столбцов by DataFrame.select_dtypes .

Используйте Series.str.len для длины теста по столбцам, а затем проверьте, все ли значения True s by DataFrame.all , имена столбцов последнего фильтра:

 df1 = df.select_dtypes(object)

mask = df1.apply(lambda x: x.str.len().eq(32)).all()
 

Если требуется проверить шестнадцатеричную строку длиной 32, используйте Series.str.contains с na параметром с True :

 mask = df1.apply(lambda x: x.str.contains(r"^[0-9a-fA-F]{32}$", na=True).all()
 

 cols = df1.columns[mask].tolist()
print (cols)
['Incident', 'Last Group']
 

Альтернативой для понимания списка является:

 cols = [c for c in df.columns if df[c].str.contains(r"^[0-9a-fA-F]{32}$", na=True).all()]
print (cols)
['Incident', 'Last Group']