Максимальное соответствие предметов из панд - PullRequest
1 голос
/ 15 мая 2019

У меня есть DataFrame. Примерно так:

| Idx | name  | age | sex | birth month | birth day |
|   - | -     | -   | -   | -           | -         |
|   0 | Mike  | 10   | w   | 8           | ?         |
|   1 | Julia | 10  | w   | ?           | ?         |
|   2 | ?     | 10  | w   | ?           | ?         |
(? : “don’t care”)
query: (age : 10, sex : w, birth month : 3 )

Я хочу найти столбец, который максимально соответствует запросу. Таким образом, ответ будет «idx 1».

Как я могу быстро найти ответ? Я только что сравнил запрос, использующий цикл. Но, должно быть, это плохой путь.

  • Я не хочу считать знак «?»!

Ответы [ 4 ]

1 голос
/ 15 мая 2019

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

| Idx | name  | age | sex | birth month | birth day |
|   - | -     | -   | -   | -           | -         |
|   0 | Mike  | ?   | m   | 8           | ?         |
|   1 | Julia | 10  | w   | ?           | ?         |
|   2 | ?     | 10  | w   | ?           | ?         |
|   3 | Julia | 10  | m   | ?           | ?         |

Если вы запросите имя = Юлия, возраст = 10, вы получите оба идентификатора (1 и 3), но если вы продолжите квалифицировать свойзапрос, чтобы спросить имя = Джулия, возраст = 10 и пол = 'w', тогда вы получите только IDX 1. Это правильно?Если так, то я думаю, что это будет работать.

import pandas as pd

df = pd.DataFrame({'Idx': [0,1,2, 3], 
    'name': ['Mike ', 'Julia ', '?', 'Julia'], 
    'sex': ['m', 'w', 'w', 'm'],
    'age': [42, 52, 52, 10]})

# Here specify the full set of parameters that makes a good match
query_params = [('name','Julia'), ('sex','w'), ('age',52)]

# Now build a mask from all of the query parameters
mask = pd.DataFrame([df[x[0]]==x[1] for x in query_params])
mask
          0      1      2
name  False  False  False
sex   False   True   True
age   False   True   True

# We'll transpose these series to make it more readable, then sum up the number of 'matches' for each row
mask = mask.T
mask['count'] = mask.sum(axis=1)
mask

    name    sex    age  count
0  False  False  False      0
1  False   True   True      2
2  False   True   True      2

# Now it's just a matter of indexing back into the original dataframe where the mask matches the most fields
df.iloc[mask['count'].idxmax()]

Idx           1
name     Julia
sex           w
age          52
0 голосов
/ 15 мая 2019

Сначала создайте dict, используя collections.defaultdict:

from collections import defaultdict

q = '(age : 10, sex : w, birth month : 3 )'
q_d = defaultdict(lambda : list('?'))
for s in re.findall('\((.+)\)', q)[0].strip().split(','):
    k, v = s.strip().split(' : ')
    q_d[k].append(v)

Таким образом, ? всегда будет существовать в сравнении.

Затем используйте pandas.DataFrame.isin:

df[df[q_d].isin(q_d).all(1)].head(1)

Выход:

  Idx   name age sex birth month birth day
2   1  Julia  10   w           ?         ?
0 голосов
/ 15 мая 2019

Должна сработать небольшая модификация исходного ответа @ Chris:

query = {'age': 10, 'sex': 'w', 'birth month': 3}
df.loc[df.eq(pd.Series(query)).sum(axis='columns').idxmax()]

Это даст вам строку с наибольшим количеством совпадений.Если есть связь, возвращается первая:

name           Julia
age               10
sex                w
birth month        ?
birth day        NaN
Name: 1, dtype: object
0 голосов
/ 15 мая 2019

Один простой способ сделать это - считать?на каждой строке в своем собственном столбце:

df['matchingscore'] = (df == '?').T.sum()
df = df.sort_values('matchingscore')

Теперь примените свой фильтр, наименьший?строки будут наверху.

Таким образом, фрейм данных становится:

    name age sex birthmonth birthday  matchingscore
0   Mike   ?   m          8        ?              2
1  Julia  10   w          ?        ?              2
2      ?  10   w          ?        ?              3

И затем применяется фильтр:

>>>df[(df.age == 10)&(df.sex == 'w')]:

    name age sex birthmonth birthday  matchingscore
1  Julia  10   w          ?        ?              2
2      ?  10   w          ?        ?              3

Единственное, что сбивает с толку, это то, что "matchingscore "инвертировано: чем ниже, тем лучше, так как это считается?поля.

...