Многократный Агрегированный Подсчет в Пандах - PullRequest
9 голосов
/ 30 апреля 2019

У меня есть DF:

data = [["John","144","Smith","200"], ["Mia","220","John","144"],["Caleb","155","Smith","200"],["Smith","200","Jason","500"]]
data_frame = pd.DataFrame(data,columns = ["Name","ID","Manager_name","Manager_ID"])
data_frame

OP:

    Name    ID    Manager_name  Manager_ID
  0 John    144    Smith             200
  1 Mia     220    John              144
  2 Caleb   155    Smith             200
  3 Smith   200    Jason             500

Я пытаюсь подсчитать количество людей, сообщивших о каждом человеке в столбце Имя.

Логика:

Подсчитайте количество людей, сообщивших индивидуально, и людей, подотчетных в цепочке.Например с Смитом;Джон и Калеб отчитываются перед Смитом, поэтому 2 + 1, а Миа отчитывается перед Джоном (который уже отчитывается перед Смитом), поэтому всего 3.

Аналогично для Джейсона -> 1, потому что Смит отчитывается перед ним, а 3 человека уже отчитываются перед Смитом.итого 4.

Я понимаю, как сделать это pythonically с некоторой рекурсией, есть ли способ эффективно сделать это в Pandas.Есть предложения?

Ожидаемый ОП:

Name        Number of people reporting
John               1
Mia                0
Caleb              0
Smith              3
Jason              4

Ответы [ 2 ]

6 голосов
/ 30 апреля 2019

Решение Скотта Бостона Networkx является предпочтительным решением ...

Есть два решения этой проблемы.Первый - векторизованное решение типа pandas, и он должен быть быстрым для больших наборов данных, второй - питоническим и не очень хорошо работает с размером набора данных, который искал OP, исходный размер df (223635,4).

  1. PANDAS SOLUTION

Эта задача состоит в том, чтобы выяснить, сколько людей управляет каждый человек в организации, включая подчиненных.Это решение создаст информационный фрейм, добавив последующие столбцы, являющиеся менеджерами предыдущих столбцов, а затем подсчитав количество сотрудников в этом фрейме, чтобы определить общее число под ними.

Сначала мы настроим вход.

import pandas as pd
import numpy as np

data = [
    ["John", "144", "Smith", "200"],
    ["Mia", "220", "John", "144"],
    ["Caleb", "155", "Smith", "200"],
    ["Smith", "200", "Jason", "500"],
]
df = pd.DataFrame(data, columns=["Name", "SID", "Manager_name", "Manager_SID"])

df = df[["SID", "Manager_SID"]]

# shortening the columns for convenience
df.columns = ["1", "2"]

print(df)

     1    2
0  144  200
1  220  144
2  155  200
3  200  500

Сначала необходимо подсчитать сотрудников без подчиненных и поместить их в отдельный словарь.

df_not_mngr = df.loc[~df['1'].isin(df['2']), '1']
non_mngr_dict = {str(key):0 for key in df_not_mngr.values}
non_mngr_dict

{'220': 0, '155': 0}

Далее мы изменим кадр данных, добавив столбцы менеджеров предыдущего столбца.Цикл останавливается, когда в самом правом столбце нет сотрудников

for i in range(2, 10):
    df = df.merge(
        df[["1", "2"]], how="left", left_on=str(i), right_on="1", suffixes=("_l", "_r")
    ).drop("1_r", axis=1)
    df.columns = [str(x) for x in range(1, i + 2)]
    if df.iloc[:, -1].isnull().all():
        break
    else:
        continue
print(df)

    1    2    3    4    5
0  144  200  500  NaN  NaN
1  220  144  200  500  NaN
2  155  200  500  NaN  NaN
3  200  500  NaN  NaN  NaN

Все столбцы, кроме первых столбцов, свернуты, а каждый сотрудник подсчитан и добавлен в словарь.

from collections import Counter

result = dict(Counter(df.iloc[:, 1:].values.flatten()))

К результату добавляется словарь без менеджера.

result.update(non_mngr_dict)
result

{'200': 3, '500': 4, nan: 8, '144': 1, '220': 0, '155': 0}
РЕКУРСИВНОЕ ПИТОНИЧЕСКОЕ РЕШЕНИЕ

Я думаю, что это, вероятно, гораздо более питон, чем вы искали.Сначала я создал список all_sids, чтобы убедиться, что мы фиксируем всех сотрудников, поскольку не все присутствуют в каждом списке.

import pandas as pd
import numpy as np

data = [
    ["John", "144", "Smith", "200"],
    ["Mia", "220", "John", "144"],
    ["Caleb", "155", "Smith", "200"],
    ["Smith", "200", "Jason", "500"],
]
df = pd.DataFrame(data, columns=["Name", "SID", "Manager_name", "Manager_SID"])

all_sids = pd.unique(df[['SID', 'Manager_SID']].values.ravel('K'))

Затем создайте сводную таблицу.

dfp = df.pivot_table(values='Name', index='SID', columns='Manager_SID', aggfunc='count')

dfp

Manager_SID  144  200  500
SID                       
144          NaN  1.0  NaN
155          NaN  1.0  NaN
200          NaN  NaN  1.0
220          1.0  NaN  NaN

Затем функция, которая будет проходить через сводную таблицу для суммирования всех отчетов..

def count_mngrs(SID, count=0):
    if str(SID) not in dfp.columns:
        return count
    else:
        count += dfp[str(SID)].sum()
        sid_list = dfp[dfp[str(SID)].notnull()].index
        for sid in sid_list:
            count = count_mngrs(sid, count)
        return count

Вызовите функцию для каждого сотрудника и распечатайте результаты.

print('SID', '     Number of People Reporting')
for sid in all_sids: 
    print(sid, "     " , int(count_mngrs(sid)))

Результаты приведены ниже, извините, я немного ленив в добавлении имен с помощью sids.

SID      Number of People Reporting
144       1
220       0
155       0
200       3
500       4

С нетерпением ждем возможности найти более подходящее решение для панд!

4 голосов
/ 30 апреля 2019

Это также проблема с графиком, и вы можете использовать Networkx :

import networkx as nx
import pandas as pd
data = [["John","144","Smith","200"], ["Mia","220","John","144"],["Caleb","155","Smith","200"],["Smith","200","Jason","500"]]
data_frame = pd.DataFrame(data,columns = ["Name","ID","Manager_name","Manager_ID"])

#create a directed graph object using nx.DiGraph
G = nx.from_pandas_edgelist(data_frame, 
                            source='Name', 
                            target='Manager_name', 
                            create_using=nx.DiGraph())

#use nx.ancestors to get set of "ancenstor" nodes for each node in the directed graph
pd.DataFrame.from_dict({i:len(nx.ancestors(G,i)) for i in G.nodes()}, 
                       orient='index', 
                       columns=['Num of People reporting'])

Выход:

       Num of People reporting
John                         1
Smith                        3
Mia                          0
Caleb                        0
Jason                        4

Draw newtorkx:

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...