Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

264
Views
¿Cómo puedo usar groupby con múltiples valores en una columna en pandas?

Tengo un marco de datos como el siguiente,

 import pandas as pd data = { 'brand': ['Mercedes', 'Renault', 'Ford', 'Mercedes', 'Mercedes', 'Mercedes', 'Renault'], 'model': ['X', 'Y', 'Z', 'X', 'X', 'X', 'Q'], 'year': [2011, 2010, 2009, 2010, 2012, 2020, 2011], 'price': [None, 1000.4, 2000.3, 1000.0, 1100.3, 3000.5, None] } df = pd.DataFrame(data) print(df) brand model year price 0 Mercedes X 2011 NaN 1 Renault Y 2010 1000.4 2 Ford Z 2009 2000.3 3 Mercedes X 2010 1000.0 4 Mercedes X 2012 1100.3 5 Mercedes X 2020 3000.5 6 Renault Q 2011 NaN

Y aquí está el otro caso para probar su solución,

 data = { 'brand': ['Mercedes', 'Mercedes', 'Mercedes', 'Mercedes', 'Mercedes'], 'model': ['X', 'X', 'X', 'X', 'X'], 'year': [2017, 2018, 2018, 2019, 2019], 'price': [None, None, None, 1000.0, 1200.50] }

Rendimiento esperado,

 brand model year price 0 Mercedes X 2017 NaN 1 Mercedes X 2018 1100.25 2 Mercedes X 2018 1100.25 3 Mercedes X 2019 1000.00 4 Mercedes X 2019 1200.50

Quiero completar los valores que faltan con el promedio de las observaciones que contienen año-1, año y año+1 y también la misma marca y modelo. Por ejemplo, el modelo Mercedes X tiene un precio nulo en 2011. Cuando miro los datos,

 2011 - 1 = 2010 2011 + 1 = 2012 The 4th observation -> Mercedes,X,2010,1000.0 The 5th observation -> Mercedes,X,2012,1100.3 The mean -> (1000.0 + 1100.3) / 2 = 1050.15

He intentado algo de la siguiente manera,

 for c_key, _ in df.groupby(['brand', 'model', 'year']): fc = ( (df['brand'] == c_key[0]) & (df['model'] == c_key[1]) & (df['year'].isin([c_key[2] + 1, c_key[2], c_key[2] - 1])) ) sc = ( (df['brand'] == c_key[0]) & (df['model'] == c_key[1]) & (df['year'] == c_key[2]) & (df['price'].isnull()) ) mean_val = df[fc]['price'].mean() df.loc[sc, 'price'] = mean_val print(df) brand model year price 0 Mercedes X 2011 1050.15 1 Renault Y 2010 1000.40 2 Ford Z 2009 2000.30 3 Mercedes X 2010 1000.00 4 Mercedes X 2012 1100.30 5 Mercedes X 2020 3000.50 6 Renault Q 2011 NaN

Pero esta solución lleva mucho tiempo para 90 000 filas y 27 columnas, entonces, ¿hay una solución más efectiva? Por ejemplo, ¿puedo usar groupby para los valores año-1, año, año+1, marca y modelo?

Gracias por adelantado.

over 4 years ago · Santiago Trujillo
5 answers
Answer question

0

Creo que en realidad una forma más eficiente sería ordenar por Brand y luego Year , y luego usar interpolate :

 df = df.sort_values(['brand', 'year']).groupby('brand').apply(lambda g: g.interpolate(limit_area='inside'))

Producción:

 >>> df brand model year price 0 Mercedes X 2011 1050.15 1 Renault Y 2010 1000.40 2 Ford Z 2009 2000.30 3 Mercedes X 2010 1000.00 4 Mercedes X 2012 1100.30 5 Mercedes X 2020 3000.50 6 Renault Q 2011 1000.40

Eso también maneja todas las columnas.

over 4 years ago · Santiago Trujillo Report

0

Basado en la solución de @richardec, pero con algún añadido para corregir el precio si se conoce el precio del próximo año. Sin embargo, no estoy seguro de si es más rápido que su solución original

 # Make an interpolated average df_out = df.sort_values(['brand', 'year']).groupby('brand').apply(lambda g: g.interpolate(limit_area='inside')) # Make an average per brand/year/model df1 = df.sort_values(['brand', 'year']).groupby(['brand','year','model']).mean().reset_index() # Check if the next line has the same brand and model. If so, take the next average price when the price isNa mask1 = df1["model"] == df1["model"].shift(-1) mask2 = df1["brand"] == df1["brand"].shift(-1) mask3 = df1["price"].isna() df1["priceCorr"] = np.where(mask1 & mask2 & mask3 ,df1["price"].shift(-1),df1["price"] ) # Merge everything together df_out = df_out.merge(df1[["brand", "year", "model","priceCorr"]], on=["brand", "year", "model"]) df_out["price"] = np.where(df_out["price"].isna(),df_out["priceCorr"], df_out["price"])
over 4 years ago · Santiago Trujillo Report

0

Aquí va una solución que parece más simple:

  1. Ordenar valores en el marco de datos original:

     df = df.sort_values(["brand", "model", "year"])
  2. Agrupa por "marca" y "modelo", y almacena los grupos en una variable (para calcular una sola vez):

     groups = df.groupby(["brand", "model"])
  3. Rellene los valores de nan utilizando el promedio de las filas anterior y siguiente ( Importante : esto supone que tiene datos de años consecutivos, lo que significa que si le faltan datos de 2015, conoce los valores de 2014 y 2016. Si no tiene datos de años consecutivos, los valores nulos seguirán siendo nulos).

     df["price"] = df["price"].fillna((groups["price"].ffill(limit=1) + groups["price"].bfill(limit=1)) / 2)

Código resultante:

 df = df.sort_values(["brand", "model", "year"]) groups = df.groupby(["brand", "model"]) df["price"] = df["price"].fillna((groups["price"].ffill(limit=1) + groups["price"].bfill(limit=1)) / 2) print(df)

Producción:

 brand model year price 2 Ford Z 2009 2000.30 3 Mercedes X 2010 1000.00 0 Mercedes X 2011 1050.15 4 Mercedes X 2012 1100.30 5 Mercedes X 2020 3000.50 6 Renault Q 2011 NaN 1 Renault Y 2010 1000.40
over 4 years ago · Santiago Trujillo Report

0

def fill_it(x): return df[(df.brand==df.iat[x,0])&(df.model==df.iat[x,1])&((df.year==df.iat[x,2]-1)|(df.year==df.iat[x,2]+1))].price.mean() df = df.apply(lambda x: x.fillna(fill_it(x.name)), axis=1) df Output 1: brand model year price 0 Mercedes X 2011 1050.15 1 Renault Y 2010 1000.40 2 Ford Z 2009 2000.30 3 Mercedes X 2010 1000.00 4 Mercedes X 2012 1100.30 5 Mercedes X 2020 3000.50 6 Renault Q 2011 NaN Output 2: brand model year price 0 Mercedes X 2017 NaN 1 Mercedes X 2018 1100.25 2 Mercedes X 2018 1100.25 3 Mercedes X 2019 1000.00 4 Mercedes X 2019 1200.50

Esto es 3 veces más rápido

 df.loc[df.price.isna(), 'price'] = df[df.price.isna()].apply(lambda x: x.fillna(fill_it(x.name)), axis=1)

Probé con otro enfoque, usando pd.rolling y es mucho más rápido (en el marco de datos con 70k filas se ejecuta en 200ms). Las salidas siguen siendo como las querías.

 df.year = pd.to_datetime(df.year, format='%Y') df.sort_values('year', inplace=True) df.groupby(['brand', 'model']).apply(lambda x: x.fillna(x.rolling('1095D',on='year', center=True).mean())).sort_index()
over 4 years ago · Santiago Trujillo Report

0

Esta no es una solución bonita, pero según su descripción, creo que funcionaría y sería muy rápido. Es solo un montón de if está dentro de un np.where en un marco de datos ordenado.

 import pandas as pd import numpy as np data = pd.DataFrame({ 'brand': ['Mercedes', 'Renault', 'Ford', 'Mercedes', 'Mercedes', 'Mercedes', 'Renault'], 'model': ['X', 'Y', 'Z', 'X', 'X', 'X', 'Q'], 'year': [2011, 2010, 2009, 2010, 2012, 2020, 2011], 'price': [None, 1000.4, 2000.3, 1000.0, 1100.3, 3000.5, None] }) data = data.sort_values(by=['brand', 'model', 'year']) data['adjusted_price'] = np.where(data['price'].isnull() & (data['brand']==data['brand'].shift(1)) & (data['brand']==data['brand'].shift(-1)) & (data['model']==data['model'].shift(1)) & (data['model']==data['model'].shift(-1)) & (data['year']==(data['year'].shift(1)+1))&(data['year']==(data['year'].shift(-1)-1)), (data['price'].shift(1)+data['price'].shift(-1))/2, data['price']) data['price'] = data['adjusted_price'] data = data.drop(['adjusted_price'], axis=1)
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!