如何根据另一个数据框中的值在数据框中创建分类字段
how to create categorical field in data frame based on values in another data frame
我需要根据另一个数据框中的值创建分类变量。考虑具有医院就诊和患者 ID 的 Table 1。请注意,一个病人可以多次到医院就诊:
+----------+------------+
| visit_id | patient_id |
+----------+------------+
| 10 | 1 |
| 20 | 1 |
| 50 | 2 |
| 100 | 3 |
| 110 | 3 |
+----------+------------+
我需要添加一个带有 1 或 0 的新字段,指示患者是否在医院就诊期间服用阿司匹林,它位于 Table 2:
+----------+------------+---------------+
| visit_id | patient_id | medication |
+----------+------------+---------------+
| 10 | 1 | aspirin |
| 10 | 1 | ibuprofin |
| 20 | 1 | codine |
| 50 | 2 | aspirin |
| 100 | 3 | ibuprofin |
| 110 | 3 | acetaminophin |
| 110 | 3 | vicodin |
+----------+------------+---------------+
你又可以看到多层次——你可以从一个医生那里得到不止一种药,对吧?当然,这只是一个例子。
我尝试合并表(内部连接),效果很好...
tab1 = pd.merge(tab1, tab2, on=['visit_id','patient_id'])
tab1['aspirin_index'] = np.where(tab1['medication'].str.contains('aspirin',
flags=re.IGNORECASE, regex=True, na=False),1,0)
...但后来我得到了同时服用阿司匹林和布洛芬的患者 1 的重复项。我只需要知道他们是否至少服用过一次阿司匹林。
+----------+------------+---------------+
| visit_id | patient_id | aspirin_index |
+----------+------------+---------------+
| 10 | 1 | 1 |
| 10 | 1 | 0 |
+----------+------------+---------------+
我需要到这里...形状与 Table 1 相同,但具有新索引。
+----------+------------+---------------+
| visit_id | patient_id | aspirin_index |
+----------+------------+---------------+
| 10 | 1 | 1 |
| 20 | 1 | 0 |
| 50 | 2 | 1 |
| 100 | 3 | 0 |
| 110 | 3 | 0 |
+----------+------------+---------------+
首先让我们设置您的示例数据。
# setup tab1 & tab2
tab1 = pd.DataFrame([[10, 1], [20, 1], [50, 2], [100, 3], [110, 3]], columns=["visit_id","patient_id"])
tab2 = pd.DataFrame([[10, 1, "aspirin"], [10, 1, "ibuprofin"], [20, 1, "codine"], [50, 2, "aspirin"], [100, 3, "ibuprofin"], [110, 3, "acetominophin"], [110, 3, "vicodin"]], columns=["visit_id","patient_id", "medication"])
有很多方法可以做到这一点。一种方法可能是将 tab2 过滤为仅阿司匹林,使用 "left" 连接将其连接到 tab1,然后用 0.
填充空值
# filter tab2 to aspirin only
# change column name
# change to 1/0 instead of text since it now only refers to aspirin
aspirin = tab2.loc[tab2.medication=="aspirin"].copy()
aspirin.columns = ["visit_id", "patient_id", "aspirin_index"]
aspirin["aspirin_index"] = 1
# left-outer merge and fill nulls
tab1 = pd.merge(tab1, aspirin, how="left", on=["visit_id","patient_id"])
tab1.aspirin_index.fillna(0, inplace=True)
tab1["aspirin_index"] = tab1.aspirin_index.astype("int")
# visit_id patient_id aspirin_index
# 10 1 1
# 20 1 0
# 50 2 1
# 100 3 0
# 110 3 0
这将为您提供一列 "aspirin_index"。这样就实现了你的目标。
但是同时使用所有药物进行同样的锻炼会怎么样...包括阿司匹林? sklearn 有一些预处理函数可以让这变得简单。
from sklearn import preprocessing
le = preprocessing.LabelEncoder()
lb = preprocessing.LabelBinarizer()
# convert each drug into a column of 1's and 0's
all_drugs = pd.DataFrame(lb.fit_transform(le.fit_transform(tab2.medication)), columns=le.classes_)
# concat with source data, aggregate, and clean up
tab2 = pd.concat((tab2.loc[:,["visit_id", "patient_id"]].copy(), all_drugs), axis=1)
tab2 = tab2.groupby(["visit_id", "patient_id"]).agg(np.sum)
tab2.reset_index(inplace=True)
# visit_id patient_id acetominophin aspirin codine ibuprofin vicodin
# 10 1 0 1 0 1 0
# 20 1 0 0 1 0 0
# 50 2 0 1 0 0 0
# 100 3 0 0 0 1 0
# 110 3 1 0 0 0 1
这是一种非常常见的方法,可以将分类数据作为二元特征的列来获取。但它占用了 space 的负载。
坚持使用包含该次就诊的每种药物的一列怎么样?这样一来,您就可以进行文本搜索,而不会为稀有药物提供大多为 0 的密集列。
# create tab1 with ALL meds taken on each visit
tab2 = tab2.groupby(["visit_id", "patient_id"]).agg({"medication": list})
tab1 = pd.merge(tab1, tab2, how="left", on=["visit_id","patient_id"])
# visit_id patient_id medication
# 10 1 [aspirin, ibuprofin]
# 20 1 [codine]
# 50 2 [aspirin]
# 100 3 [ibuprofin]
# 110 3 [acetominophin, vicodin]
# helper function to extract records for ANY drug
def drug_finder(drug):
idx = tab1.medication.apply(lambda drugs: drug in drugs)
return tab1.loc[idx].copy()
# find aspirin
drug_finder("aspirin")
# visit_id patient_id medication
# 10 1 [aspirin, ibuprofin]
# 50 2 [aspirin]
# find ibuprofin
drug_finder("ibuprofin")
# visit_id patient_id medication
# 10 1 [aspirin, ibuprofin]
# 100 3 [ibuprofin]
我需要根据另一个数据框中的值创建分类变量。考虑具有医院就诊和患者 ID 的 Table 1。请注意,一个病人可以多次到医院就诊:
+----------+------------+
| visit_id | patient_id |
+----------+------------+
| 10 | 1 |
| 20 | 1 |
| 50 | 2 |
| 100 | 3 |
| 110 | 3 |
+----------+------------+
我需要添加一个带有 1 或 0 的新字段,指示患者是否在医院就诊期间服用阿司匹林,它位于 Table 2:
+----------+------------+---------------+
| visit_id | patient_id | medication |
+----------+------------+---------------+
| 10 | 1 | aspirin |
| 10 | 1 | ibuprofin |
| 20 | 1 | codine |
| 50 | 2 | aspirin |
| 100 | 3 | ibuprofin |
| 110 | 3 | acetaminophin |
| 110 | 3 | vicodin |
+----------+------------+---------------+
你又可以看到多层次——你可以从一个医生那里得到不止一种药,对吧?当然,这只是一个例子。
我尝试合并表(内部连接),效果很好...
tab1 = pd.merge(tab1, tab2, on=['visit_id','patient_id'])
tab1['aspirin_index'] = np.where(tab1['medication'].str.contains('aspirin',
flags=re.IGNORECASE, regex=True, na=False),1,0)
...但后来我得到了同时服用阿司匹林和布洛芬的患者 1 的重复项。我只需要知道他们是否至少服用过一次阿司匹林。
+----------+------------+---------------+
| visit_id | patient_id | aspirin_index |
+----------+------------+---------------+
| 10 | 1 | 1 |
| 10 | 1 | 0 |
+----------+------------+---------------+
我需要到这里...形状与 Table 1 相同,但具有新索引。
+----------+------------+---------------+
| visit_id | patient_id | aspirin_index |
+----------+------------+---------------+
| 10 | 1 | 1 |
| 20 | 1 | 0 |
| 50 | 2 | 1 |
| 100 | 3 | 0 |
| 110 | 3 | 0 |
+----------+------------+---------------+
首先让我们设置您的示例数据。
# setup tab1 & tab2
tab1 = pd.DataFrame([[10, 1], [20, 1], [50, 2], [100, 3], [110, 3]], columns=["visit_id","patient_id"])
tab2 = pd.DataFrame([[10, 1, "aspirin"], [10, 1, "ibuprofin"], [20, 1, "codine"], [50, 2, "aspirin"], [100, 3, "ibuprofin"], [110, 3, "acetominophin"], [110, 3, "vicodin"]], columns=["visit_id","patient_id", "medication"])
有很多方法可以做到这一点。一种方法可能是将 tab2 过滤为仅阿司匹林,使用 "left" 连接将其连接到 tab1,然后用 0.
填充空值# filter tab2 to aspirin only
# change column name
# change to 1/0 instead of text since it now only refers to aspirin
aspirin = tab2.loc[tab2.medication=="aspirin"].copy()
aspirin.columns = ["visit_id", "patient_id", "aspirin_index"]
aspirin["aspirin_index"] = 1
# left-outer merge and fill nulls
tab1 = pd.merge(tab1, aspirin, how="left", on=["visit_id","patient_id"])
tab1.aspirin_index.fillna(0, inplace=True)
tab1["aspirin_index"] = tab1.aspirin_index.astype("int")
# visit_id patient_id aspirin_index
# 10 1 1
# 20 1 0
# 50 2 1
# 100 3 0
# 110 3 0
这将为您提供一列 "aspirin_index"。这样就实现了你的目标。
但是同时使用所有药物进行同样的锻炼会怎么样...包括阿司匹林? sklearn 有一些预处理函数可以让这变得简单。
from sklearn import preprocessing
le = preprocessing.LabelEncoder()
lb = preprocessing.LabelBinarizer()
# convert each drug into a column of 1's and 0's
all_drugs = pd.DataFrame(lb.fit_transform(le.fit_transform(tab2.medication)), columns=le.classes_)
# concat with source data, aggregate, and clean up
tab2 = pd.concat((tab2.loc[:,["visit_id", "patient_id"]].copy(), all_drugs), axis=1)
tab2 = tab2.groupby(["visit_id", "patient_id"]).agg(np.sum)
tab2.reset_index(inplace=True)
# visit_id patient_id acetominophin aspirin codine ibuprofin vicodin
# 10 1 0 1 0 1 0
# 20 1 0 0 1 0 0
# 50 2 0 1 0 0 0
# 100 3 0 0 0 1 0
# 110 3 1 0 0 0 1
这是一种非常常见的方法,可以将分类数据作为二元特征的列来获取。但它占用了 space 的负载。
坚持使用包含该次就诊的每种药物的一列怎么样?这样一来,您就可以进行文本搜索,而不会为稀有药物提供大多为 0 的密集列。
# create tab1 with ALL meds taken on each visit
tab2 = tab2.groupby(["visit_id", "patient_id"]).agg({"medication": list})
tab1 = pd.merge(tab1, tab2, how="left", on=["visit_id","patient_id"])
# visit_id patient_id medication
# 10 1 [aspirin, ibuprofin]
# 20 1 [codine]
# 50 2 [aspirin]
# 100 3 [ibuprofin]
# 110 3 [acetominophin, vicodin]
# helper function to extract records for ANY drug
def drug_finder(drug):
idx = tab1.medication.apply(lambda drugs: drug in drugs)
return tab1.loc[idx].copy()
# find aspirin
drug_finder("aspirin")
# visit_id patient_id medication
# 10 1 [aspirin, ibuprofin]
# 50 2 [aspirin]
# find ibuprofin
drug_finder("ibuprofin")
# visit_id patient_id medication
# 10 1 [aspirin, ibuprofin]
# 100 3 [ibuprofin]