【问题标题】:1 to 2 matching in two dataframes with different sizes in Python/R在 Python/R 中两个不同大小的数据帧中进行 1 到 2 匹配
【发布时间】:2022-07-03 07:05:40
【问题描述】:

请帮我解决这个我整天都在苦苦挣扎的问题哈哈,Python 或 R 的解决方案都很好! 请帮助我真的卡住了!!!

我有两个数据框 - df1 有 44 行,df2 有 100 行,它们都有这些列: ID、身份 (0,1)、年龄、性别、种族、民族、身高、体重

对于 df1 中的每一行,我需要在 df2 中找到一个 age 匹配项:

  1. 可以是精确的年龄匹配,但应该使用的标准是 - df2[age]-5
  2. 我需要一个列表/字典来存储 df1 的年龄匹配项及其 ID
  3. 然后我需要从df2中随机选择2个ID作为df1年龄的最终匹配
  4. 我还需要确保 2 个 df2 匹配项与 df1 具有相同的性别和种族

我尝试过 R 和 Python,但都停留在嵌套循环部分。 我不确定如何遍历 df1 和 df2 的每条记录,将 df1 age 与 df2 age-5 和 df2 age+5 进行比较,并存储匹配项

以下是 df1 和 df2 的示例数据格式: |身份证 |性别 |年龄 |种族 | | -------- | -------------- |--------|--------| | 284336 |女| 42.8 | 2 | | 294123 |男| 48.5 | 1 |

这是我在 R 中尝试过的:

id_match <- NULL
for (i in 1:nrow(gwi_case)){
  age <- gwi_case$age[i]
  gender <- gwi_case$gender[i]
  ethnicity <- gwi_case$hispanic_non[i]
  race <- gwi_case$race[i]
  
  x <- which(gwi_control$gender==gender & gwi_control$age>=age-5 & gwi_control$age<=age+5 & gwi_control$hispanic_non==ethnicity & gwi_control$race==race)
  
  y <- sample(x, min(2, length(x)))
  
  id_match <- c(id_match, y)
}

id_match <- id_match[!duplicated(id_match)]
length(id_match)

【问题讨论】:

  • 您能向我们展示您尝试使用哪种语言的尝试吗?
  • 不要分享图片。分享数据
  • 在 R 中,这称为非等连接。搜索那个。你会找到解决问题的方法
  • @onyambu 我已经做了一些研究,不幸的是这不是我在这里寻找的
  • @C.Nivs 见上面的代码,谢谢!

标签: python r dataframe nested matching


【解决方案1】:

问题是这样问的:

  • 对于df1 中的每一行,在df2 中找到一个年龄匹配项,这样df2[age] - 5 &lt;= df1[age] &lt;= df2[age] + 5
  • 创建一个列表/字典来保存 df1 的年龄匹配和 ID
  • 从df2中随机选择2个ID作为df1年龄的最终匹配

下面是一些 Python 代码:

  • 使用条件来填充列表列表ageMatches,其中包含与每个唯一df1 年龄匹配的唯一df2 年龄列表
  • df2 上为df1 中的每个年龄调用DataFrame.query(),以使用df2 ID 的列表填充idMatches,其中年龄与每个唯一的df1 年龄匹配
  • 使用唯一的 df1 年龄键和 2 个(或更少,如果可用数字 df2 匹配年龄的 ID 的列表填充 age1ToID2
  • df1 添加一列,其中包含与每行年龄对应的一对选定df2 ID(即age1ToID2 中的值)
import pandas as pd
import numpy as np
df1 = pd.DataFrame({'ID':list(range(101,145)), 'Age':[v % 11 + 21 for v in range(44)], 'Height':[67]*44})
df2 = pd.DataFrame({'ID':list(range(1,101)), 'Age':[v % 10 + 14 for v in range(50)] + [v % 20 + 25 for v in range(0,100,2)], 'Height':[67]*100})

ages1 = np.sort(df1['Age'].unique())
ages2 = np.sort(df2['Age'].unique())
ageMatches = [[] for _ in ages1]
j1, j2 = 0, 0
for i, age1 in enumerate(ages1):
    while j1 < len(ages2) and ages2[j1] < age1 - 5:
        j1 += 1
    if j2 <= j1:
        j2 = j1 + 1
    while j2 < len(ages2) and ages2[j2] <= age1 + 5:
        j2 += 1
    ageMatches[i] += list(ages2[j1:j2])
idMatches = [df2.query('Age in @m')['ID'].to_list() for i, m in enumerate(ageMatches)]

# select random pair of df2 IDs for each unique df1 age and put them into a new df1 column
from random import sample
age1ToID2 = {ages1[i]:m if len(m) < 2 else sample(m, 2) for i, m in enumerate(idMatches)}
df1['df2_matches'] = df1['Age'].apply(lambda x: age1ToID2[x])
print(df1)

输出:

     ID  Age  Height df2_matches
0   101   21      67    [24, 30]
1   102   22      67    [50, 72]
2   103   23      67    [10, 37]
3   104   24      67    [63, 83]
4   105   25      67    [83, 49]
5   106   26      67    [20, 52]
6   107   27      67    [49, 84]
7   108   28      67    [54, 55]
8   109   29      67    [91, 55]
9   110   30      67    [65, 51]
10  111   31      67    [75, 72]
11  112   21      67    [24, 30]
...
42  143   30      67    [65, 51]
43  144   31      67    [75, 72]

这有望提供 OP 要求的结果和中间集合,或者足够接近以达到所需结果的东西。

或者,要让df1 中每一行的随机选择不同,我们可以这样做:

# select random pair of df2 IDs for each df1 row and put them into a new df1 column
from random import sample
age1ToID2 = {ages1[i]:m for i, m in enumerate(idMatches)}
def foo(x):
    m = age1ToID2[x]
    return m if len(m) < 2 else sample(m, 2)
df1['df2_matches'] = df1['Age'].apply(foo)
print(df1)

输出:

     ID  Age  Height df2_matches
0   101   21      67    [71, 38]
1   102   22      67     [71, 5]
2   103   23      67     [9, 38]
3   104   24      67    [49, 61]
4   105   25      67    [27, 93]
5   106   26      67    [40, 20]
6   107   27      67     [9, 19]
7   108   28      67    [53, 72]
8   109   29      67    [82, 53]
9   110   30      67    [74, 62]
10  111   31      67    [52, 62]
11  112   21      67    [71, 39]
...
42  143   30      67    [96, 66]
43  144   31      67    [63, 83]

【讨论】:

  • 非常感谢!!!这正是我要问的!您如何确保 df1 和 df2 中的 2 个选择之间的性别相同?
  • 我也更新了我的问题供您参考! @constantstranger
  • 很高兴听到这有助于回答您的原始问题。随意将答案标记为“已接受”
  • 嗨!我很乐意接受您的回答,但我还有一个问题,请重新阅读我的帖子,非常感谢您的帮助!
  • @Jessica Leung 自从我回答你的问题以来,你已经大大改变了你的问题。如果您将修改后的问题作为链接到该问题的新问题发布,我将很乐意查看它,但是由于我已经花时间帮助您解决原始问题,您指出这正是您要问的问题,感谢您接受答案(有关上下文,请参阅this post)。当然,这完全取决于您。
【解决方案2】:

不确定我是否完全理解要求,但是...在 python 中,您可以使用 apply 到数据框和 lambda 函数来执行一些时髦的事情

df1['age_matched_ids'] = df1.apply(lambda x: list(df2.loc[df2['Age'] >= x['Age'] - 5 & df2['Age'] <= x['Age'] + 5, 'ID']), axis=1)

这会将 df2 中年龄 +/- 5 之间的 ID 列表存储在“age_matched_ids”列中。您可以从此处执行 #2 和 #3。

【讨论】:

  • 谢谢!但它引发了这个错误:ValueError:一个系列的真值是模棱两可的。使用 a.empty、a.bool()、a.item()、a.any() 或 a.all()。
  • 使用 & 而不是 'and'.... 要添加我的答案,它应该对问题进行排序。
  • 谢谢!刚刚也用我上面的代码更新了问题
【解决方案3】:

我一直在寻找相同的东西,但我的问题是如何创建一个条件,以便匹配的一对应该在 10 岁以内和 10 岁以内的身高差,我需要我的代码来找到匹配的随机匹配的一对达到这两个条件。谢谢

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多