Funciones de ventana similares a SQL en PANDAS: Numeración de filas en el dataframe de Python Pandas

Vengo de un fondo de sql y uso el siguiente paso de procesamiento de datos con frecuencia:

  1. Partición de la tabla de datos por uno o más campos
  2. Para cada partición, agregue un número a cada una de sus filas que clasifique la fila por uno o más campos, donde el analista especifica ascendente o descendente

EX:

df = pd.DataFrame({'key1' : ['a','a','a','b','a'], 'data1' : [1,2,2,3,3], 'data2' : [1,10,2,3,30]}) df data1 data2 key1 0 1 1 a 1 2 10 a 2 2 2 a 3 3 3 b 4 3 30 a 

Estoy buscando cómo hacer el equivalente de PANDAS a esta función de ventana de sql:

 RN = ROW_NUMBER() OVER (PARTITION BY Key1, Key2 ORDER BY Data1 ASC, Data2 DESC) data1 data2 key1 RN 0 1 1 a 1 1 2 10 a 2 2 2 2 a 3 3 3 3 b 1 4 3 30 a 4 

He intentado lo siguiente que he llegado a trabajar donde no hay ‘particiones’:

 def row_number(frame,orderby_columns, orderby_direction,name): frame.sort_index(by = orderby_columns, ascending = orderby_direction, inplace = True) frame[name] = list(xrange(len(frame.index))) 

Intenté extender esta idea para trabajar con particiones (grupos en pandas) pero lo siguiente no funcionó:

 df1 = df.groupby('key1').apply(lambda t: t.sort_index(by=['data1', 'data2'], ascending=[True, False], inplace = True)).reset_index() def nf(x): x['rn'] = list(xrange(len(x.index))) df1['rn1'] = df1.groupby('key1').apply(nf) 

Pero acabo de recibir muchos NaN cuando hago esto.

Idealmente, habría una forma sucinta de replicar la función de la función de ventana de sql (he descubierto los agregados basados ​​en la ventana … eso es un liner en pandas) … alguien puede compartir conmigo la forma más idiomática de ¿Numerosas filas como esta en PANDAS?

Puedes hacer esto usando groupby dos veces junto con el método de rank :

 In [11]: g = df.groupby('key1') 

Utilice el argumento del método min para dar a los valores que comparten los mismos datos1 el mismo RN:

 In [12]: g['data1'].rank(method='min') Out[12]: 0 1 1 2 2 2 3 1 4 4 dtype: float64 In [13]: df['RN'] = g['data1'].rank(method='min') 

Y luego agrupe estos resultados y agregue el rango con respecto a data2:

 In [14]: g1 = df.groupby(['key1', 'RN']) In [15]: g1['data2'].rank(ascending=False) - 1 Out[15]: 0 0 1 0 2 1 3 0 4 0 dtype: float64 In [16]: df['RN'] += g1['data2'].rank(ascending=False) - 1 In [17]: df Out[17]: data1 data2 key1 RN 0 1 1 a 1 1 2 10 a 2 2 2 2 a 3 3 3 3 b 1 4 3 30 a 4 

Se siente como que debería haber una forma nativa de hacer esto (¡puede que haya! …).

también puede usar sort_values() , groupby() y finalmente cumcount() + 1 :

 df['RN'] = df.sort_values(['data1','data2'], ascending=[True,False]) \ .groupby(['key1']) \ .cumcount() + 1 print(df) 

rendimientos

  data1 data2 key1 RN 0 1 1 a 1 1 2 10 a 2 2 2 2 a 3 3 3 3 b 1 4 3 30 a 4 

PS probado con pandas 0.18

Puedes usar transform y Rank juntos. Aquí hay un ejemplo.

 df = pd.DataFrame({'C1' : ['a','a','a','b','b'], 'C2' : [1,2,3,4,5]}) df['Rank'] = df.groupby(by=['C1'])['C2'].transform(lambda x: x.rank()) df 

introduzca la descripción de la imagen aquí

Echa un vistazo al método Pandas Rank para más información.

pandas.lib.fast_zip() puede crear una matriz de tuplas a partir de una lista de matrices. Puedes usar esta función para crear una serie de tuplas y luego clasificarla:

 values = {'key1' : ['a','a','a','b','a','b'], 'data1' : [1,2,2,3,3,3], 'data2' : [1,10,2,3,30,20]} df = pd.DataFrame(values, index=list("abcdef")) def rank_multi_columns(df, cols, **kw): data = [] for col in cols: if col.startswith("-"): flag = -1 col = col[1:] else: flag = 1 data.append(flag*df[col]) values = pd.lib.fast_zip(data) s = pd.Series(values, index=df.index) return s.rank(**kw) rank = df.groupby("key1").apply(lambda df:rank_multi_columns(df, ["data1", "-data2"])) print rank 

el resultado:

 a 1 b 2 c 3 d 2 e 4 f 1 dtype: float64