#python #pandas #group-by
Вопрос:
У меня есть фрейм данных Pandas (df) с 3 столбцами: «Лекарство» , «Влияние» , «Журнал». Каждая строка представляет собой научную статью, и в каждой статье указывается, какое влияние оказывает лекарство на болезнь. Пример:
Drug Affect Journal aspirin downregulate paper1 aspririn downregulate paper2 aspirin upregulate paper3 aspirin neutral paper4 aspirin downregulate paper5 aspririn upregulate paper6 iboruprofen upregulate paper7 iboruprofen upregulate paper8 iboruprofen downregulate paper9 other_drug ... ...
Результат должен выглядеть следующим образом:
Лекарственный
аспирин
Я хочу сделать фильтр df, чтобы получать только те лекарства, которые в большинстве работ показывают, что они снижают уровень заболевания, поэтому, если, например, у аспирина есть 3 статьи, в которых говорится, что он снижает уровень, и 2 статьи, в которых говорится, что он повышает уровень, и 1, в котором говорится, что он оказывает нейтральное действие, то мы предполагаем, что большинство доказательств показывают, что эффект заключается в снижении. Поэтому я хочу, чтобы в моем отфильтрованном df был аспирин. Поэтому иборупрофен не должен попадать в мой отфильтрованный df.
Как мне попасть в группу? Спасибо за вашу помощь!
Ответ №1:
Сгруппируйте по препарату и влиянию, чтобы подсчитать количество записей.
df_grouped = df.groupby(["Drug","Affect"], as_index=False).count()
Вывод:
Drug Affect Journal
0 aspirin downregulate 3
1 aspirin neutral 1
2 aspirin upregulate 2
3 iboruprofen downregulate 1
4 iboruprofen upregulate 2
Просмотрите список уникальных лекарств и проверьте номера. Если «Понизить» > «повысить», добавьте этот препарат в список результатов.
result_drugs = []
for drug in np.unique(df["Drug"]:
numberOfdownregulate = df_grouped[(df_grouped["Drug"] == drug) amp; (df_grouped["Affect"] == "downregulate")].Journal.values[0]
numberOfupregulate = df_grouped[(df_grouped["Drug"] == drug) amp; (df_grouped["Affect"] == "upregulate")].Journal.values[0]
result = numberOfdownregulate - numberOfupregulate
if result > 0:
result_drugs.append(drug)
Наконец, отфильтруйте исходный кадр данных.
df[df["Drug"].isin(result_drugs)]
Если вы хотите иметь только фрейм данных с желаемыми значениями лекарств.
pd.DataFrame(result_drugs, columns=["Drug"])
Конечный результат:
Drug
0 aspirin
Ответ №2:
Альтернативным вариантом было бы использование convtools
библиотеки:
from convtools import conversion as c
# fmt: off
input_data = [
{'Drug': 'aspirin', 'Affect': 'downregulate', 'Journal': 'paper1'},
{'Drug': 'aspririn', 'Affect': 'downregulate', 'Journal': 'paper2'},
{'Drug': 'aspirin', 'Affect': 'upregulate', 'Journal': 'paper3'},
{'Drug': 'aspirin', 'Affect': 'neutral', 'Journal': 'paper4'},
{'Drug': 'aspirin', 'Affect': 'downregulate', 'Journal': 'paper5'},
{'Drug': 'aspririn', 'Affect': 'upregulate', 'Journal': 'paper6'},
{'Drug': 'iboruprofen', 'Affect': 'upregulate', 'Journal': 'paper7'},
{'Drug': 'iboruprofen', 'Affect': 'upregulate', 'Journal': 'paper8'},
{'Drug': 'iboruprofen', 'Affect': 'downregulate', 'Journal': 'paper9'}
]
# fmt: on
converter = (
c.group_by(c.item("Drug"))
.aggregate(
{
"drug": c.item("Drug"),
"down": c.ReduceFuncs.Count(
where=c.item("Affect") == "downregulate"
),
"up": c.ReduceFuncs.Count(where=c.item("Affect") == "upregulate"),
"neutral": c.ReduceFuncs.Count(
where=c.item("Affect") == "neutral"
),
}
)
.filter(
c.and_(
c.item("down") > c.item("up"),
c.item("down") > c.item("neutral"),
)
)
# after the above the result is:
# [{"drug": "aspirin", "down": 2, "up": 1, "neutral": 1}]
.iter(c.item("drug"))
.as_type(list)
.gen_converter()
)
assert converter(input_data) == ["aspirin"]
Ответ №3:
Более простой подход с использованием filter
и value_counts
:
(
df.groupby("Drug")
.filter(lambda x: x["Affect"].value_counts().nlargest(1).index[0] == "downregulate")
.get(["Drug"])
.drop_duplicates()
)
values_counts
возвращает значения для каждого уникального значения в Affect
столбце.
nlargest(1)
возвращает одну строку с наибольшим количеством
index
возвращает массив с именем наиболее часто встречающегося элемента