Ускорение панд. Применить функцию. Подсчет строк и работа с ними - PullRequest
0 голосов
/ 14 сентября 2018

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

Для начала исходная база данных CSV имеет следующую структуру:

1 строка = 1 человек

          Age       Sex      City_start      City_destination  ...

человек 1

человек 2

.....

Конечная структура базы данных:

         Balance_2004    Balance_2005       ....

City1

City2

....

Для расчета этого баланса по городам и годам, которые я создалфункция, которая фильтрует исходную базу данных, чтобы подсчитать, сколько строк имеет определенный город в city_destination (INs), сколько строк в city_start (OUTs), а затем простая сумма для расчета баланса как INs - OUTs:

 # idb = initial database
 # City1 = pre-existing in final database

 def get_balance(city, df):
    ins = idb.City_start[idb.City_start == City1].count()
    outs = idb.City_destination[idb.City_destination == City1].count()
    balance = ins - outs
    return balance

Затем с помощью этой функции я использовал pandas apply для заполнения окончательной базы данных следующим образом:

# fdb = final database

fdb['Balance_2004'] = idb['City_start'].apply(get_balance, df=idb)

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

Есть ли способ сделать это менее трудоемким способом?

Заранее спасибо

Ответы [ 2 ]

0 голосов
/ 14 сентября 2018

Возможно, имеет смысл выполнить этот расчет только один раз, сгруппировав по городам:

def get_balance_all_cities(df):
    df_diff = pd.DataFrame([df.groupby(["City_start"])["Name"].count(),
                            df.groupby(["City_destination"])["Name"].count()]).T
    df_diff.columns = "start", "end"
    df_diff[df_diff.isna()] = 0
    return df_diff.start - df_diff.end

Вот несколько примеров того, как это работает:

>>> df = pd.DataFrame([("Person 1", "Chicago", "Chicago"), ("Person 2", "New York", "Chicago"), ("Person 3", "Houston", "New York")], columns=["Name", "City_start", "City_destination"])

>>> df
       Name City_start  City_destination
0  Person 1    Chicago           Chicago
1  Person 2   New York           Chicago
2  Person 3    Houston          New York

>>> ins = df.groupby(["City_start"])["Name"].count()
City_start
Chicago     1
Houston     1
New York    1
Name: Name, dtype: int64

>>> outs = df.groupby(["City_end"])["Name"].count()
City_destination
Chicago     2
New York    1
Name: Name, dtype: int64

>>> df_diff = pd.DataFrame([ins, outs]).T
>>> df_diff.columns = "start", "end"
>>> df_diff[df_diff.isna()] = 0
>>> balance = df_diff.start - df_diff.end
Chicago    -1.0
Houston     1.0
New York    0.0
dtype: float64

Работа-в конце концов, речь идет о городах, в которых никто не живет в конце или в начале, но живет в другое время.

0 голосов
/ 14 сентября 2018

Я считаю, что нужно агрегировать на cities с year с с DataFrameGroupBy.size и изменить на unstack, затем вычесть на sub и при необходимости преобразуйте в integer с:

idb = pd.DataFrame([("a", "Chicago", "Chicago", 2018),
                    ("b", "New York", "Chicago", 2018), 
                    ("c", "New York", "Chicago", 2017),
                    ("d", "Houston", "LA", 2018)], 
        columns=["Name", "City_start", "City_destination", 'year'])
print (idb) 
  Name City_start City_destination  year
0    a    Chicago          Chicago  2018
1    b   New York          Chicago  2018
2    c   New York          Chicago  2017
3    d    Houston               LA  2018


a1 = idb.groupby(["City_start", 'year']).size().unstack(fill_value=0)
a2 = idb.groupby(["City_destination", 'year']).size().unstack(fill_value=0)

idb = a1.sub(a2, fill_value=0).astype(int).add_prefix('Balance_')
print (idb)
year      Balance_2017  Balance_2018
Chicago             -1            -1
Houston              0             1
LA                   0            -1
New York             1             1
...