Me gustaría promediar ciertos valores de columna dependiendo de si se cumple una condición en otra columna. Específicamente, si la columna 1 en el marco de datos a continuación es <1700, quiero incluir el valor correspondiente en esa fila de la columna 51 en mi cálculo promedio. Y si la columna 2 <1700, también quiero incluir el valor en esa fila de la columna 52 en mi cálculo promedio.

Entonces, para la fila 0, la nueva columna calculada para esa fila sería 64 (promedio de 65 y 63). Para la fila 1, el promedio sería solo 80 (valor de la columna 51) ya que ni las columnas 2 ni 3 eran menores de 1700 y, por lo tanto, no se incluyeron en el cálculo promedio.

Este es un ejemplo simplificado ya que mi marco de datos real tiene aproximadamente 10 columnas para condiciones con 10 columnas de valores correspondientes para promediar.

Como posible complejidad, los encabezados de las columnas son números en lugar de etiquetas de texto tradicionales y no se refieren al orden de esa columna en el marco de datos, ya que he excluido ciertas columnas cuando importé el archivo csv. En otras palabras, la columna 51 no es la columna 51 en el marco de datos.

Cuando ejecuto el siguiente código, aparece el siguiente error:

ValueError: ("Ningún eje llamado 1 para el tipo de objeto", 'ocurrió en el índice 0')

¿Hay una manera más eficiente de codificar esto y evitar este error? ¡Gracias por tu ayuda!

import pandas as pd
import numpy as np

test_df = pd.DataFrame({1:[1600,1600,1600,1700,1800],2:[1500,2000,1400,1500,2000],
3:[2000,2000,2000,2000,2000],51:[65,80,75,80,75],52:[63,82,85,85,75],53:[83,80,75,76,78]})

test_df

     1     2     3   51  52  53
0  1600  1500  2000  65  63  83
1  1600  2000  2000  80  82  80
2  1600  1400  2000  75  85  75
3  1700  1500  2000  80  85  76
4  1800  2000  2000  75  75  78


def calc_mean_based_on_conditions(row):

        list_of_columns_to_average = []
        for i in range(1,4):
            if row[i] < 1700:
                list_of_columns_to_average.append(i+50)

        if not list_of_columns_to_average:
            return np.nan
        else:
            return row[(list_of_columns_to_average)].mean(axis=1)

test_df['MeanValue'] = test_df.apply(calc_mean_based_on_conditions, axis=1)
3
newcoder 2 oct. 2019 a las 17:48

3 respuestas

La mejor respuesta

Algo muy relevante (que admite int como nombres de columna) - https://github.com/theislab/anndata / temas / 31

Debido a este error / problema, convertí los nombres de columna para escribir string:

test_df = pd.DataFrame({'1':[1600,1600,1600,1700,1800],'2':[1500,2000,1400,1500,2000],
'3':[2000,2000,2000,2000,2000],'51':[65,80,75,80,75],'52':[63,82,85,85,75],'53': 
[83,80,75,76,78]})

Creó un nuevo marco de datos - new_df para cumplir con los requisitos

new_df = test_df[['1', '2', '3']].where(test_df[['1','2','3']]<1700).notnull()

New_df ahora se ve así

       1      2      3
0   True   True  False
1   True  False  False
2   True   True  False
3  False   True  False
4  False  False  False

Luego simplemente cambie el nombre de la columna y verifique usando 'where'

new_df = new_df.rename(columns={"1": "51", "2":"52", "3":"53"})
test_df['mean_value'] = test_df[['51', '52', '53']].where(new_df).mean(axis=1)

Esto debería darle el resultado deseado:

    1     2     3  51  52  53  mean_value
0  1600  1500  2000  65  63  83        64.0
1  1600  2000  2000  80  82  80        80.0
2  1600  1400  2000  75  85  75        80.0
3  1700  1500  2000  80  85  76        85.0
4  1800  2000  2000  75  75  78         NaN
2
hkr 2 oct. 2019 a las 20:43

Eliminé mi otra respuesta porque estaba yendo por el camino equivocado. Lo que desea hacer es generar una máscara de sus columnas condicionales, luego usar esa máscara para aplicar una función a otras columnas. En este caso, 1 corresponde a 51, 2 a 52, etc.

import pandas as pd
import numpy as np

test_df = pd.DataFrame({1:[1600,1600,1600,1700,1800],2:[1500,2000,1400,1500,2000],
3:[2000,2000,2000,2000,2000],51:[65,80,75,80,75],52:[63,82,85,85,75],53:[83,80,75,76,78]})

test_df

     1     2     3   51  52  53
0  1600  1500  2000  65  63  83
1  1600  2000  2000  80  82  80
2  1600  1400  2000  75  85  75
3  1700  1500  2000  80  85  76
4  1800  2000  2000  75  75  78



# create dictionary to map columns to one another
l1=list(range(1,4))
l2=list(range(50,54))
d = {k:v for k,v in zip(l1,l2)}

d
{1: 51, 2: 52, 3: 53}

temp=test_df[l1] > 1700 # Subset initial dataframe, generate mask
for _, row in temp.iterrows(): #iterate through subsetted data
    list_of_columns_for_mean=list() # list of columns for later computation
    for k, v in d.items(): #iterate through each k:v and evaluate conditional for each row
        if row[k]:
            list_of_columns_for_mean.append(v)
            # the rest should be pretty easy to figure out

Esta no es una solución elegante, pero es una solución. Desafortunadamente, se me acabó el tiempo para dedicarlo, pero espero que esto te lleve a una mejor dirección.

1
HelpfulHound 2 oct. 2019 a las 16:15

Probablemente haya una forma mejor y vectorizada de hacerlo, pero podría hacerlo sin la función

import numpy as np
import pandas as pd
from collections import defaultdict

test_df = pd.DataFrame({1:[1600,1600,1600,1700,1800],2:[1500,2000,1400,1500,2000],
3:[2000,2000,2000,2000,2000],51:[65,80,75,80,75],52:[63,82,85,85,75],53:[83,80,75,76,78]})

# List of columns that you're applying the condition to
condition_cols = list(range(1,4))

# Get row and column indices where this condition is true
condition = np.where(test_df[condition_cols].lt(1700))

# make a dictionary mapping row to true columns
cond_map = defaultdict(list)
for r,c in zip(*condition):
    cond_map[r].append(c)

# Get the means of true columns
means = []
for row in range(len(test_df)):
    if row in cond_map:
        temp = []
        for col in cond_map[row]:
            # Needs 51 because of Python indexing starting at zero + 50
            temp.append(test_df.loc[row, col+51])
        means.append(temp)
    else:
        # If the row has no true columns (i.e row 4)
        means.append(np.nan)

test_df['Means'] = [np.mean(l) for l in means]   

El problema es indexar columnas de filas verdaderas y de forma vectorizada.

1
HS-nebula 2 oct. 2019 a las 19:16
58203963