Комплекс панд питона группируется по

#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 возвращает массив с именем наиболее часто встречающегося элемента