Pandas эквивалент точки dplyr - PullRequest
1 голос
/ 28 января 2020

Прошу прощения за довольно веское объяснение, но надеюсь, что вы поймете эту идею.

Я пользователь R, и я считаю, что возможности Tidyverse в обработке данных действительно мощные. Но недавно я начал изучать Python, и в частности pandas, чтобы расширить свои возможности в анализе данных. Инстинктивно я пытаюсь сделать что-то в pandas, как я делал это, когда использовал dplyr.

Поэтому мой вопрос заключается в том, является ли какой-либо эквивалент точки dplyr при использовании цепочки методов в pandas.

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

Пример R:

require(dplyr)
require(purrr)
test_df = data.frame(group = rep(c(1,2,3), each = 3),
                     data= c(1:9))
test_df %>%
group_by(group) %>%
mutate(., min_of_max = map_dbl(data, ~data[data > .x] %>% min())) %>%
mutate(., min_of_max_2 = map_dbl(min_of_max, ~min_of_max[min_of_max > .x] %>% min()))

Вывод:

# A tibble: 9 x 4
# Groups:   group [3]
group  data min_of_max min_of_max_2
<dbl> <int>      <dbl>        <dbl>
1     1     1          2            3
2     1     2          3          Inf
3     1     3        Inf          Inf
4     2     4          5            6
5     2     5          6          Inf
6     2     6        Inf          Inf
7     3     7          8            9
8     3     8          9          Inf
9     3     9        Inf          Inf

Я знаю этот dplyr даже не требует точки, но я поставил его для лучшего понимания специфики c моего вопроса

То же самое в Pandas

Неверный пример:

import pandas as pd
import numpy as np
test_df = (
    pd.DataFrame({'A': np.array([1,2,3]*3), 'B': np.array(range(1,10))})
    .sort_values(by = ['A', 'B'])
)
(test_df.assign(min_of_max = test_df.apply(lambda x: (test_df.B[(test_df.B > x.B) &
                                                           (test_df.A[test_df.A == x.A])]).min(), axis = 1))
    .assign(min_of_max2 = 'assume_dot_here'.apply(lambda x: (test_df.min_of_max[(test_df.min_of_max > x.min_of_max) &
                                                           (test_df.A[test_df.A == x.A])]).min(), axis = 1)))

В этом примере поместить точку в секунду .assign было бы отличной способностью, но она не работает в pandas.

Действительный пример, который разрушает цепочку:

test_df = test_df.assign(min_of_max = test_df.apply(lambda x: 
(test_df.B[(test_df.B > x.B) & (test_df.A[test_df.A == x.A])]).min(), axis = 1))

test_df = test_df.assign(min_of_max2 = test_df.apply(lambda x : 
(test_df.min_of_max[(test_df.min_of_max > x.min_of_max) & (test_df.A[test_df.A 
== x.A])]).min(), axis = 1))

Вывод:

   A  B  min_of_max  min_of_max2
0  1  1         4.0          7.0
3  1  4         7.0          NaN
6  1  7         NaN          NaN
1  2  2         5.0          8.0
4  2  5         8.0          NaN
7  2  8         NaN          NaN
2  3  3         6.0          9.0
5  3  6         9.0          NaN
8  3  9         NaN          NaN

Так есть ли какой-нибудь удобный способ вызвать объект из предыдущей части цепочки в секунду .assign? Поскольку при использовании test_df.apply() во втором .assign будет использоваться исходный test_df без вычислений test_df['min_of_max']

Извините за нечитаемый код в Python, я все еще выясняю, как писать более ясно.

Ответы [ 2 ]

1 голос
/ 28 января 2020

В Pandas выполните цепочку из двух вызовов assign, но сделайте это любым способом, который не зависит от исходного контекста фрейма данных, например, с вызовом DataFrame.apply. Ниже используется список, эквивалентный значениям индекса:

test_df = pd.DataFrame({'group': np.repeat([1,2,3],3), 'data': np.arange(1,10)})

(
   test_df.assign(min_of_max = lambda x: [np.min(x["data"].loc[(x["data"] > x["data"].iloc[i]) &
                                                               (x["group"] == x["group"].iloc[i])]
                                                ) for i in test_df.index.values])
          .assign(min_of_max_2 = lambda x: [np.min(x["min_of_max"].loc[(x["min_of_max"] > x["min_of_max"].iloc[i]) &
                                                                       (x["group"] == x["group"].iloc[i])]
                                                  ) for i in test_df.index.values])
)

#    group  data  min_of_max  min_of_max_2
# 0      1     1         2.0           3.0
# 1      1     2         3.0           NaN
# 2      1     3         NaN           NaN
# 3      2     4         5.0           6.0
# 4      2     5         6.0           NaN
# 5      2     6         NaN           NaN
# 6      3     7         8.0           9.0
# 7      3     8         9.0           NaN
# 8      3     9         NaN           NaN

Однако, так же, как вы можете комбинировать назначения в dplyr::mutate, вы можете сделать то же самое, комбинируя DataFrame.assign вызовы с использованием метода lambda (не путать с lambda в DataFrame.apply).

R

test_df <- data.frame(group = rep(c(1,2,3), each = 3), data = c(1:9))

test_df %>%
  group_by(group) %>%
  mutate(min_of_max = map_dbl(data, ~data[data > .x] %>% min()),
         min_of_max_2 = map_dbl(min_of_max, ~min_of_max[min_of_max > .x] %>% min()))

# # A tibble: 9 x 4
# # Groups:   group [3]
#   group  data min_of_max min_of_max_2
#   <dbl> <int>      <dbl>        <dbl>
# 1     1     1          2            3
# 2     1     2          3          Inf
# 3     1     3        Inf          Inf
# 4     2     4          5            6
# 5     2     5          6          Inf
# 6     2     6        Inf          Inf
# 7     3     7          8            9
# 8     3     8          9          Inf
# 9     3     9        Inf          Inf

Pandas

test_df = pd.DataFrame({'group': np.repeat([1,2,3],3), 'data': np.arange(1,10)})

test_df.assign(min_of_max = lambda x: [np.min(x["data"].loc[(x["data"] > x["data"].iloc[i]) &
                                                            (x["group"] == x["group"].iloc[i])]
                                             ) for i in test_df.index.values],
               min_of_max_2 = lambda x: [np.min(x["min_of_max"].loc[(x["min_of_max"] > x["min_of_max"].iloc[i]) &
                                                                    (x["group"] == x["group"].iloc[i])]
                                               ) for i in test_df.index.values])

#    group  data  min_of_max  min_of_max_2
# 0      1     1         2.0           3.0
# 1      1     2         3.0           NaN
# 2      1     3         NaN           NaN
# 3      2     4         5.0           6.0
# 4      2     5         6.0           NaN
# 5      2     6         NaN           NaN
# 6      3     7         8.0           9.0
# 7      3     8         9.0           NaN
# 8      3     9         NaN           NaN

Кстати, поскольку Pandas, возможно, смоделировано после R много лет go Уэсом МакКинни (см. paper ), база R имеет тенденцию быть более переводимой к Pandas. Ниже within отражает использование списка assign и sapply списка зеркал.

База R

test_df <- within(test_df, {      
  min_of_max <- sapply(1:nrow(test_df), 
                       function(i) min(data[data > data[i] & 
                                            group == group[i]]))

  min_of_max_2 <- sapply(1:nrow(test_df), 
                         function(i) min(min_of_max[min_of_max > min_of_max[i] & 
                                                    group == group[i]]))      
})

test_df[c("group", "data", "min_of_max", "min_of_max_2")]

#   group data min_of_max min_of_max_2
# 1     1    1          2            3
# 2     1    2          3          Inf
# 3     1    3        Inf          Inf
# 4     2    4          5            6
# 5     2    5          6          Inf
# 6     2    6        Inf          Inf
# 7     3    7          8            9
# 8     3    8          9          Inf
# 9     3    9        Inf          Inf
0 голосов
/ 30 января 2020

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

(test_df.assign(min_of_max = test_df.apply(lambda x: (test_df.B[(test_df.B > x.B) &
                                                     (test_df.A[test_df.A == x.A])]).min(), axis = 1))
        .assign(min_of_max2 = lambda y: y.apply(lambda x: (y.min_of_max[(y.min_of_max > x.min_of_max) &
                                                          (y.A[y.A == x.A])]).min(), axis = 1))) 

Передача 'lambda y' во второй .assign будет рассматривать y как выход из предыдущей части в цепочке

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