@Override
  public void writeDataset(UsersDataset usersDataset, String fileName) throws IOException {

    String fileNameWithExtension = fileName;
    if (!fileNameWithExtension.endsWith(".csv")) {
      fileNameWithExtension = fileNameWithExtension + "." + CSV_EXTENSION;
    }

    StringBuilder stringBuilder = new StringBuilder();

    Feature[] features = usersDataset.getFeatures();

    stringBuilder.append(ID_ITEM_COLUMN_NAME).append(columnDelimiter);
    stringBuilder.append(ITEM_NAME_COLUMN_NAME);

    for (Feature userFeature : features) {
      String extendedName = userFeature.getExtendedName();
      stringBuilder
          .append(columnDelimiter)
          .append(stringDelimiter)
          .append(extendedName)
          .append(stringDelimiter);
    }

    stringBuilder.append(registerDelimiter);

    for (User user : usersDataset) {

      int idUser = user.getId();
      StringBuilder linea = new StringBuilder();

      // Escribir el id y el nombre
      linea
          .append(idUser)
          .append(columnDelimiter)
          .append(stringDelimiter)
          .append(user.getName())
          .append(stringDelimiter);
      for (Feature userFeature : features) {
        Object featureValue = user.getFeatureValue(userFeature);
        String featureValueString = userFeature.getType().featureValueToString(featureValue);
        linea.append(columnDelimiter).append(featureValueString);
      }

      linea.append(registerDelimiter);
      stringBuilder.append(linea.toString());
    }

    BufferedWriter bufferedWriter =
        new BufferedWriter(new FileWriter(new File(fileNameWithExtension)));
    bufferedWriter.write(stringBuilder.toString());
    bufferedWriter.flush();
    bufferedWriter.close();
  }
  /**
   * Añade un item al perfil para que sea tomado en cuenta como valorado positivamente (relevante)
   * en el perfil de usuario.
   *
   * @param items contenido del item que se desea agregar
   * @param condensationFormula formula para agregar los características numéricos de los items
   *     relevantes en el perfil
   */
  public void addItems(Set<Item> items, AggregationOperator condensationFormula) {
    TreeMap<Feature, Set<Number>> profileNumericalValues = new TreeMap<Feature, Set<Number>>();

    for (Item i : items) {
      for (Feature itemFeature : i.getFeatures()) {

        switch (itemFeature.getType()) {
          case Nominal:
            Object featureValue = i.getFeatureValue(itemFeature);
            if (_nominalValues.containsKey(itemFeature)) {
              Map<Object, Double> treeMap = _nominalValues.get(itemFeature);
              if (treeMap.containsKey(featureValue)) {
                treeMap.put(featureValue, treeMap.get(featureValue) + 1);
              } else {
                treeMap.put(featureValue, 1.0);
              }
            } else {
              Map<Object, Double> treeMap = new TreeMap<Object, Double>();
              treeMap.put(featureValue, 1.0);
              _nominalValues.put(itemFeature, treeMap);
            }
            break;

          case Numerical:
            if (profileNumericalValues.containsKey(itemFeature)) {
              profileNumericalValues.get(itemFeature).add((Number) i.getFeatureValue(itemFeature));
            } else {
              Set<Number> lista = new TreeSet<Number>();
              lista.add((Number) i.getFeatureValue(itemFeature));
              profileNumericalValues.put(itemFeature, lista);
            }
            break;
          default:
            throw new UnsupportedOperationException(
                "The item feature type " + itemFeature.getType() + " isn't supported");
        }
      }
    }

    for (Feature f : _nominalValues.keySet()) {
      for (Object value : _nominalValues.get(f).keySet()) {
        double valor = _nominalValues.get(f).get(value);
        valor = valor / items.size();
        _nominalValues.get(f).put(value, valor);
      }
    }

    _numericalValues = new TreeMap<Feature, Double>();
    for (Feature f : profileNumericalValues.keySet()) {
      Set<Number> get = profileNumericalValues.get(f);
      _numericalValues.put(f, condensationFormula.aggregateValues(get));
    }
  }
 /**
  * Devuelve los valores de la característica especificada definidos en este perfil de usuario.
  *
  * @param itemFeature Característica para los que se busca sus valores.
  * @return Conjunto de valores de la característica. Si no contiene la característica, devuelve
  *     null.
  */
 @Override
 public Set<Object> getValuedFeatureValues(Feature itemFeature) {
   if (itemFeature.getType() == FeatureType.Nominal) {
     return _nominalValues.get(itemFeature).keySet();
   }
   if (itemFeature.getType() == FeatureType.Numerical) {
     Set<Object> ret = new TreeSet<Object>();
     ret.add(_numericalValues.get(itemFeature));
     return ret;
   }
   throw new UnsupportedOperationException(
       "The item feature type " + itemFeature.getType() + " isn't supported");
 }
 @Override
 public boolean contains(Feature itemFeature, Object featureValue) {
   boolean ret;
   switch (itemFeature.getType()) {
     case Nominal:
       if (_nominalValues.containsKey(itemFeature)) {
         ret = _nominalValues.get(itemFeature).containsKey(featureValue);
       } else {
         ret = false;
       }
       break;
     case Numerical:
       ret = _numericalValues.containsKey(itemFeature);
       break;
     default:
       throw new UnsupportedOperationException(
           "The item feature type " + itemFeature.getType() + " isn't supported");
   }
   return ret;
 }
  /**
   * Devuelve el valor del perfil para el valor concreto de la característica.
   *
   * @param itemFeature Característica para la que se busca el valor.
   * @param value Valor de la característica para el que se busca el balor.
   * @return Valor del perfil para el valor y la característica indicados.
   * @throws IllegalArgumentException Si no encuentra la característica o el valor de la misma.
   */
  @Override
  public double getFeatureValueValue(Feature itemFeature, Object value) {
    if (itemFeature.getType() == FeatureType.Nominal) {
      if (_nominalValues.containsKey(itemFeature)) {
        Map<Object, Double> get = _nominalValues.get(itemFeature);
        if (get.containsKey(value)) {
          return get.get(value);
        }
      } else {
        throw new IllegalArgumentException("Undefined nominal item feature " + itemFeature);
      }
    }

    if (itemFeature.getType() == FeatureType.Numerical) {
      if (_numericalValues.containsKey(itemFeature)) {
        return _numericalValues.get(itemFeature);
      } else {
        throw new IllegalArgumentException("Undefined numerical item feature " + itemFeature);
      }
    }
    throw new UnsupportedOperationException("Unknow item feature type " + itemFeature.getType());
  }