Обновление списка объектов Python

#python #arrays #python-3.x #list #loops

Вопрос:

У меня есть объект со структурой, похожей на:

 models = [{
  'name': 'Model 1',
  'errors': {
    'mse': None,
    'rmse': None
  }
},
{
  'name': 'Model 2',
  'errors': {
    'mse': None,
    'rmse': None
  }
}]
 

Теперь я хочу пройтись по объекту и что-то изменить, поэтому я попытался использовать что-то вроде этого:

 for index, model in enumerate(models):
  # Carry out model performance and update the respective errors
  model['errors']['mse'] = metrics.mean_squared_error(...)
 

Что происходит, так это то, что все объекты внутри models списка обновляются значениями последнего элемента. Я тоже пытался использовать это (и это тоже не сработало).:

 for index in range(len(models)):
  # Carry out model performance and update the respective errors
  models[index]['errors']['mse'] = metrics.mean_squared_error(...)
 

Если я даже напишу models[0]['errors']['mse'] = 10 , все остальные индексы также будут обновлены. Может ли кто-нибудь, пожалуйста, помочь мне с решением этой проблемы?

(Было бы действительно здорово, если бы вы могли предложить решение, в котором я мог бы обновлять, просматривая список)

Фактический Код

 import importlib

class Models:
    
    # Model structure
    model = {
        'base': None,
        'name': None,
        'model': None,
        'config': {},
        'errors': {
            'mae': None,
            'mse': None,
            'rmse': None,
        },
        'scores': {
            'r2': None,
            'accuracy_score': None
        }
    }
    
    # Prints Looping Progress
    def progress(self, index):
        return '('   str(index 1)   '/'   str(self.models.__len__())   ')'
    
    # Import Models
    def import_models(self):
        for model in self.models:
            model['model'] = getattr(importlib.import_module(model['base']), model['name'])(**model['config'])
    
    # Restructure Models Object
    def restructure_models(self, models):
        restructured_models = []
        for model in models:
            restructured_model = {**self.model, **model}
            restructured_models.append(restructured_model)
        return restructured_models
    
    # Constructor
    def __init__(self, models):
        self.models = self.restructure_models(models)
        self.import_models()
    
    # Train Models
    def fit(self, X_train, y_train):
        for index, model in enumerate(self.models):
            print('Training '   self.progress(index)   ': ', model['name'])
            model['model'].fit(X_train, y_train)
        print('n')
        
    # Predict Values
    def predict(self, X_test):
        all_predictions = []
        for index, model in enumerate(self.models):
            print('Running '   self.progress(index)   ': ', model['name'])
            # Predict
            predictions = model['model'].predict(X_test)
            all_predictions.append({
                'model': model['name'],
                'predictions': predictions
            })
        print('n')
        return all_predictions
            
    # Evaluate Trained Models
    def test(self, X_test, y_test):
        from sklearn import metrics
        all_predictions = []
        for index, model in enumerate(self.models):
            print('Evaluating '   self.progress(index)   ': ', model['name'])
            # Predict
            predictions = model['model'].predict(X_test)
            # Errors
            model['errors']['mae'] = metrics.mean_absolute_error(y_test, predictions)
            model['errors']['mse'] = metrics.mean_squared_error(y_test, predictions)
            model['errors']['rmse'] = np.sqrt(model['errors']['mse'])
            # Scores
            model['scores']['r2'] = metrics.r2_score(y_test, predictions)
            model['scores']['accuracy_score'] = metrics.r2_score(y_true = y_test, y_pred = predictions)
            all_predictions.append({
                'model': model['name'],
                'predictions': predictions
            })
        print('n')
        return all_predictions
    
    # Evaluated Performance Metrics
    def results(self):
        for model in self.models:
            print('Model: ', model['name'])
            print('MAE: ', model['errors']['mae'])
            print('MSE: ', model['errors']['mse'])
            print('RMSE: ', model['errors']['rmse'])
            print('R2: ', model['scores']['r2'])
            print('Accuracy Score: ', model['scores']['accuracy_score'])
            print('n')
 

Вызов и использование класса:

 selected_models = [
    {
        'base': 'sklearn.linear_model',
        'name': 'LinearRegression'
    },
    {
        'base': 'sklearn.ensemble',
        'name': 'RandomForestRegressor',
                'config': {
                        'n_estimators': 50
                }
    }
]
models = Models(models = selected_models)
predictions = models.test(X_test = X_test_scaled, y_test = y_test)
 

Проблема заключается в test self.models неправильном обновлении функции (все элементы списка изменяются в соответствии с последним элементом).

Комментарии:

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

2. Я обновил вопрос с помощью фактического кода

Ответ №1:

Проблема в том, что здесь:

 restructured_model = {**self.model, **model}
 

Вы создаете новый dict , но он ссылается на синглтон Models.model .

Я думаю, ты это имел в виду:

 import copy

copy_model = copy.deepcopy(self.model)
restructured_model = {**copy_model, **model}
 

Комментарии:

1. Вау, это сработало как заклинание! Большое вам спасибо за помощь!

Ответ №2:

Можете ли вы попробовать это для отладки?

 for index, model in enumerate(models):
  # Carry out model performance and update the respective errors
  set_value = metrics.mean_squared_error(...)
  print('setting value to '   str(set_value))
  model['errors']['mse'] = set_value
 

Комментарии:

1. Я добавил его, и результаты снова те же. При печати внутри цикла он выдает правильные значения, однако после запуска цикла, когда я печатаю весь объект, все элементы обновляются со значением последнего элемента.