来自 pandas 的最大项目匹配
Maximum item matching from pandas
我有一个数据框。
像这样:
| 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”。
如何快速找到答案?
我只是比较了使用 for 循环的查询。
但一定是方法不对。
- 我不想数“?”马克!
一个简单的方法是计算 ?在其自己的列中的每一行:
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" 是倒置的:越低越好,因为它很重要?字段。
首先使用 collections.defaultdict
创建 dict
:
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 ? ?
对@Chris 的原始答案稍作修改应该可以:
query = {'age': 10, 'sex': 'w', 'birth month': 3}
df.loc[df.eq(pd.Series(query)).sum(axis='columns').idxmax()]
这会让您获得匹配次数最多的行。如果有平局,returns 第一个:
name Julia
age 10
sex w
birth month ?
birth day NaN
Name: 1, dtype: object
如果我对问题的理解正确,那么您在指定列中匹配最多的行中查找的内容。以你为例(但进一步扩展)
| Idx | name | age | sex | birth month | birth day |
| - | - | - | - | - | - |
| 0 | Mike | ? | m | 8 | ? |
| 1 | Julia | 10 | w | ? | ? |
| 2 | ? | 10 | w | ? | ? |
| 3 | Julia | 10 | m | ? | ? |
如果您查询 name = Julia, age=10,您将同时获得 idx(1 和 3),但如果您进一步限定查询以询问 name= Julia, age=10 和 sex ='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
我有一个数据框。 像这样:
| 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”。
如何快速找到答案? 我只是比较了使用 for 循环的查询。 但一定是方法不对。
- 我不想数“?”马克!
一个简单的方法是计算 ?在其自己的列中的每一行:
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" 是倒置的:越低越好,因为它很重要?字段。
首先使用 collections.defaultdict
创建 dict
:
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 ? ?
对@Chris 的原始答案稍作修改应该可以:
query = {'age': 10, 'sex': 'w', 'birth month': 3}
df.loc[df.eq(pd.Series(query)).sum(axis='columns').idxmax()]
这会让您获得匹配次数最多的行。如果有平局,returns 第一个:
name Julia
age 10
sex w
birth month ?
birth day NaN
Name: 1, dtype: object
如果我对问题的理解正确,那么您在指定列中匹配最多的行中查找的内容。以你为例(但进一步扩展)
| Idx | name | age | sex | birth month | birth day |
| - | - | - | - | - | - |
| 0 | Mike | ? | m | 8 | ? |
| 1 | Julia | 10 | w | ? | ? |
| 2 | ? | 10 | w | ? | ? |
| 3 | Julia | 10 | m | ? | ? |
如果您查询 name = Julia, age=10,您将同时获得 idx(1 和 3),但如果您进一步限定查询以询问 name= Julia, age=10 和 sex ='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