функциональное программирование и датафреймы Python Pandas в конвейерах - PullRequest
0 голосов
/ 06 февраля 2019

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

Вот 2 идеи, но надеждачто-то лучше существует:)

идея № 1 - нет функционального программирования, но экономится память

def foo(df, param):
    df['col'] = df['col'] + param

def pipeline(df):
    foo(df, 1)
    foo(df, 2)
    foo(df, 3)

идея № 2 - более функциональное программирование, но тратится память на выполнение .copy ()

def foo(df, param):
    df = df.copy()
    df['col'] = df['col'] + param
    return df

def pipeline(df):
    df1 = foo(df, 1)
    df2 = foo(df1, 2)
    df3 = foo(df2, 3)

Ответы [ 2 ]

0 голосов
/ 06 февраля 2019

Если ваш фрейм данных является одномерным (то есть это список элементов), тогда вы можете вместо применения нескольких «каналов» делать все за один раз.Например, нам дают фрейм данных

>>> table
     Name  Year of Birth      City
0    Mike           1970  New York
1   Chris           1981     Miami
2  Janine           1975   Seattle

Мы хотим рассчитать возраст и проверить, живем ли кто-нибудь в Майами.Вы можете применить 2 трубы, но это можно сделать с помощью одной лямбды:

>>> f = lambda s: pd.Series([2019 - s['Year of Birth'], s['City'] == 'Miami'])

Затем примените

>>> table[['Age', 'Lives in Miami']] = table.apply(f, axis=1)
>>> table
     Name  Year of Birth      City  Age  Lives in Miami
0    Mike           1970  New York   49           False
1   Chris           1981     Miami   38            True
2  Janine           1975   Seattle   44           False

Вся работа выполняется в f.

Другой пример: скажем, вы хотите проверить, кому за 45 лет.Он содержит две операции serial (предыдущий пример был 2 paralel операции):

  1. Расчет возраста
  2. Проверьте, больше ли возраст, чем45

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

>>> table
     Name  Year of Birth      City   
0    Mike           1970  New York            
1   Chris           1981     Miami           
2  Janine           1975   Seattle      
>>> g = lambda s: pd.Series([2019 - s['Year of Birth'] > 45])              
>>> table['Older than 45'] = table.apply(g, axis=1)
>>> table
     Name  Year of Birth      City  Older than 45
0    Mike           1970  New York           True
1   Chris           1981     Miami          False
2  Janine           1975   Seattle          False

Если вы предпочитаете неизменность, тогда выполните

>>> table
     Name  Year of Birth      City
0    Mike           1970  New York
1   Chris           1981     Miami
2  Janine           1975   Seattle 
>>> pd.concat([table, table.apply(f, axis=1)], axis=1)
     Name  Year of Birth      City   0      1
0    Mike           1970  New York  49  False
1   Chris           1981     Miami  38   True
2  Janine           1975   Seattle  44  False

Если вам не нравятся лямбды, тогда определите

def f(s):
    return pd.Series([2019 - s['Year of Birth'], 
                      s['City'] == 'Miami'])

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

(И в конце концов вы понимаете, что вам лучше использовать Haskell.)

0 голосов
/ 06 февраля 2019

Вы можете связывать вызовы функций, работающие на кадре данных.Также взгляните на DataFrame.pipe в пандах.Примерно так, добавив пару операций не-foo:

df = (df.pipe(foo,1)
      .pipe(foo,2)
      .pipe(foo,3)
      .drop(columns=['drop','these'])
      .assign(NEW_COL=lambda x: x['OLD_COL'] / 10))

df будет первым аргументом, переданным foo при использовании pipe.

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