Pandas — определение значений фрейма данных, которые начинаются со значения в списке

#python #pandas

#python #pandas

Вопрос:

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

 >>> import pandas as pd
>>> d=pd.DataFrame()
>>> d['A']=['12345','12354','76','4']
>>> d['B']=['4442','2345','33','5']
>>> d['C']=['5553','4343','33','5']
>>> d
       A     B     C
0  12345  4442  5553
1  12354  2345  4343
2     76    33    33
3      4     5     5
 

И скажем, у меня есть 3 интересующих значения:

 >>> vals=['123','76']
 

Мне интересно определить, какие значения в моем фрейме данных начинаются с любого из значений в моем списке. В моем примере есть 3 случая: (0, A) начинается с 123; (1,A) начинается с 123; и (2, A) начинается с 76.

Есть ли способ, которым я могу это сделать, не перебирая каждое из моих значений?

Если бы я был заинтересован в точном сопоставлении значений, я мог бы просто сделать:

 >>> d.isin(vals)
       A      B      C
0  False  False  False
1  False  False  False
2   True  False  False
3  False  False  False
>>> 
 

И если бы меня интересовало, начинаются ли значения с 1 определенного значения, я мог бы сделать:

 >>> d.applymap(lambda x:x.startswith('123'))
       A      B      C
0   True  False  False
1   True  False  False
2  False  False  False
3  False  False  False
>>> 
 

Но как я могу объединить эти два, чтобы найти любое значение, которое начинается с любого значения в моем списке?

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

1. вы можете сделать df.apply(lambda x: x.str.contains('|'.join(vals)))

Ответ №1:

Вы можете создать шаблон регулярных выражений и проверять каждый столбец по очереди, используя apply с помощью вызова лямбда str.contains :

 In [9]:
vals=['123','76']
v = ['^'   x for x in vals]
d.apply(lambda x: x.str.contains('|'.join(v)))

Out[9]:
       A      B      C
0   True  False  False
1   True  False  False
2   True  False  False
3  False  False  False
 

Результирующий шаблон регулярных выражений:

 In [10]:
'|'.join(v)

Out[10]:
'^123|^76'
 

Обновить

На самом деле вы можете сделать это с помощью stack and unstack , чтобы сначала объединить все столбцы в один столбец, вызвать str.contains шаблон регулярных выражений, а затем unstack вернуться к исходной форме:

 In [9]:
vals=['123','76']
v = ['^'   x for x in vals]
d.stack().str.contains('|'.join(v)).unstack()

Out[9]:
       A      B      C
0   True  False  False
1   True  False  False
2   True  False  False
3  False  False  False
 

Это более чистый способ сделать это по сравнению с использованием apply

Ответ №2:

Вы могли бы сделать это:

 d.applymap(lambda x: any([x.startswith(v) for v in vals]))
 

Ответ №3:

Альтернативное решение, которое не использует .apply() :

 In [66]: search_re = '^(?:{})'.format('|'.join(vals))

In [67]: search_re
Out[67]: '^(?:123|76)'

In [69]: df.astype(str).stack().str.match(search_re).unstack()
Out[69]:
       A      B      C
0   True  False  False
1   True  False  False
2   True  False  False
3  False  False  False
 

Ответ №4:

Немного сложно, но это кажется самым быстрым решением.

Вы можете использовать str.startswith , который работает только с сериями, поэтому используйте list понимание и concat вывод. Но нужно проверить больше элементов в списке vals , поэтому используйте другое понимание списка numpy.logical_or , с reduce которым работает numpy array — преобразование вывода concat и последнее создание Dataframe с тем же столбцом и индексом, что и оригинал, и вывод данных numpy.logical_or :

 print ([pd.concat([d[col].str.startswith(i) for col in d], axis=1).values for i in vals])
[array([[ True, False, False],
       [ True, False, False],
       [False, False, False],
       [False, False, False]], dtype=bool), array([[False, False, False],
       [False, False, False],
       [ True, False, False],
       [False, False, False]], dtype=bool)]

print (np.logical_or.reduce(
      [pd.concat([d[col].str.startswith(i) for col in d], axis=1).values for i in vals]))
[[ True False False]
 [ True False False]
 [ True False False]
 [False False False]]

print(pd.DataFrame(np.logical_or.reduce(
      [pd.concat([d[col].strstartswith(i) for col in d], 
                 axis=1).values for i in vals]), 
                   index=d.index, columns=d.columns))

       A      B      C
0   True  False  False
1   True  False  False
2   True  False  False
3  False  False  False
 

Тайминги:

 #[40000 rows x 3 columns]
d = pd.concat([d]*10000).reset_index(drop=True)


In [77]: %timeit (d.applymap(lambda x: any([x.startswith(v) for v in vals])))
1 loop, best of 3: 228 ms per loop

In [78]: %timeit (d.apply(lambda x: x.str.contains('|'.join(['^'   x for x in vals]))))
10 loops, best of 3: 147 ms per loop

In [79]: %timeit (d.astype(str).stack().str.match('^(?:{})'.format('|'.join(vals))).unstack())
10 loops, best of 3: 172 ms per loop

In [80]: %timeit (pd.DataFrame(np.logical_or.reduce([pd.concat([d[col].str.startswith(i) for col in d], axis=1).values for i in vals]), index=d.index, columns=d.columns))
10 loops, best of 3: 116 ms per loop