Сначала давайте настроим ваши образцы данных.
# 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 только до аспирина, присоединить его к 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
Это довольно распространенный подход для получения категориальных данных в виде столбцов двоичных объектов.Но это занимает много места.
Как насчет того, чтобы придерживаться одного столбца, в котором указаны все лекарства для этого посещения в списке?Это позволило бы вам выполнять поиск текста и не иметь плотных столбцов, состоящих в основном из 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]