#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