Tengo una tabla de valores de datos que se ve así:

Score ID
1.2    1
2.4    1
1.1    1
1.9    1
2.4    2
3.5    2
2.2    2
1.1    3
1.1    3
1.7    3
3.1    3
2.2    3

Y lo que realmente necesito es calcular el cambio en la puntuación para cada ID en una columna separada, de modo que se vea así:

Score   ID  Changes
1.2     1   
2.4     1   1.2
1.1     1   -1.3
1.9     1   0.8
2.4     2   
3.5     2   1.1
2.2     2   -1.3
1.1     3   
1.1     3   0
1.7     3   0.6
3.1     3   1.4
2.2     3   -0.9

Observe cómo se calcula cada "cambio" tomando la puntuación anterior y restándola de la puntuación actual: p. en los primeros cambios, obtienes 1.2 tomando 1.2 = 2.4 (puntaje actual) - 1.2 (El puntaje anterior)

El problema es que estos puntajes solo deben pertenecer a los puntajes dentro de los límites de la ID. No puede simplemente repetir y tomar la diferencia de puntajes. ¿Cómo haría esto? ¿Qué lógica puedo usar para probar que contiene "cambios" dentro de la misma ID? Estoy familiarizado con R o python (o BASH) para hacer esto.

0
Tom 11 may. 2016 a las 23:53

4 respuestas

La mejor respuesta

En Python usando Pandas:

import pandas as pd

df = pd.DataFrame(
        {'ID': [1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3],
         'Score': [1.2, 2.4, 1.1, 1.9, 2.4, 3.5, 2.2, 1.1, 1.1, 1.7, 3.1, 2.2]})

df['Changes'] = df.groupby('ID').Score.transform(lambda group: group.diff())

>>> df
    Score  ID  Changes
0     1.2   1      NaN
1     2.4   1      1.2
2     1.1   1     -1.3
3     1.9   1      0.8
4     2.4   2      NaN
5     3.5   2      1.1
6     2.2   2     -1.3
7     1.1   3      NaN
8     1.1   3      0.0
9     1.7   3      0.6
10    3.1   3      1.4
11    2.2   3     -0.9
3
Alexander 11 may. 2016 a las 21:00

En R quizás:

transform(
  df, 
  Changes = ave(df$Score, df$ID, FUN = function(x) c(NA, diff(x)))
)
#    Score ID Changes
# 1    1.2  1      NA
# 2    2.4  1     1.2
# 3    1.1  1    -1.3
# 4    1.9  1     0.8
# 5    2.4  2      NA
# 6    3.5  2     1.1
# 7    2.2  2    -1.3
# 8    1.1  3      NA
# 9    1.1  3     0.0
# 10   1.7  3     0.6
# 11   3.1  3     1.4
# 12   2.2  3    -0.9

df contiene su marco de datos original.


Con respecto al comentario de @ Bulat:

id<- c(1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3)
score <-  c(1.2, 2.4, 1.1, 1.9, 2.4, 3.5, 2.2, 1.1, 1.1, 1.7, 3.1, 2.2)
library(data.table)
df <- data.frame(id, score)
dt = data.table(id, score)
library(microbenchmark)
microbenchmark(
  dt = dt[ , diff := c(NA, diff(score)), by = id],
  df = df$diff <- ave(df$score, df$id, FUN = function(x) c(NA, diff(x))),
  times = 1000
)
# Unit: microseconds
#  expr      min        lq      mean    median        uq      max neval cld
#    dt 1121.931 1225.2660 1342.4626 1269.5530 1321.2210 5908.411  1000   b
#    df  397.175  488.2085  547.8198  525.8175  586.7995 7784.270  1000  a 
5
lukeA 11 may. 2016 a las 22:38

R con data.table:

id<- c(1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3)
score <-  c(1.2, 2.4, 1.1, 1.9, 2.4, 3.5, 2.2, 1.1, 1.1, 1.7, 3.1, 2.2)
library(data.table)
df = data.table(id, score)

df[ , diff := c(NA, diff(score)), by = id]

Vale la pena mencionar que esto es 10 veces más rápido en comparación con el enfoque transform:

id<- c(1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3)
id <- rep(id, 10^5)
score <-  c(1.2, 2.4, 1.1, 1.9, 2.4, 3.5, 2.2, 1.1, 1.1, 1.7, 3.1, 2.2)
score <- rep(score, 10^5)
library(data.table)
dt = data.table(id, score)
df = data.frame(id, score)

library(microbenchmark)
m <- microbenchmark(
  "data.table" = dt[ , diff := c(NA, diff(score)), by = id],
  "transform" = transform(
    df, 
    Changes = ave(df$score, df$id, FUN = function(x) c(NA, diff(x)))
  ), times = 10
)

boxplot(m)
print(m)
# Unit: milliseconds
#      expr       min        lq      mean    median        uq       max neval
# data.table   95.1905  100.3342  111.2434  102.6525  106.7417  151.2913    10
#  transform 1032.3829 1066.3912 1078.5727 1070.9577 1103.4971 1135.7380    10
2
Bulat 11 may. 2016 a las 22:25

Usando dplyr en R

library(dplyr)
df1 %>% 
   group_by(ID) %>% 
   mutate(diff = c(NA, diff(Score)))
#   Score    ID  diff
#   <dbl> <int> <dbl>
#1    1.2     1    NA
#2    2.4     1   1.2
#3    1.1     1  -1.3
#4    1.9     1   0.8
#5    2.4     2    NA
#6    3.5     2   1.1
#7    2.2     2  -1.3
#8    1.1     3    NA
#9    1.1     3   0.0
#10   1.7     3   0.6
#11   3.1     3   1.4
#12   2.2     3  -0.9
0
akrun 12 may. 2016 a las 01:28