Как разложить вектор признаков на Java? - PullRequest
0 голосов
/ 15 января 2020

У меня есть следующий фрейм данных:

+---------------+--------------------+
|IndexedArtistID|     recommendations|
+---------------+--------------------+
|           1580|[[919, 0.00249262...|
|           4900|[[41749, 7.143963...|
|           5300|[[0, 2.0147272E-4...|
|           6620|[[208780, 9.81092...|
+---------------+--------------------+

Я хочу разбить столбец рекомендаций так, чтобы получить фрейм данных следующим образом:

+---------------+--------------------+
|IndexedArtistID|     recommendations|
+---------------+--------------------+
|           1580|919                 |
|           1580|0.00249262          |
|           4900|41749               |
|           4900|7.143963            |
|           5300|0                   |
|           5300|2.0147272E-4        |
|           6620|208780              |
|           6620|9.81092             |
+---------------+--------------------+

Итак, я хочу разбить вектор объектов на столбцы, а затем объединить эти столбцы в один столбец. Часть слияния описана в: Как разбить одну строку на несколько строк в Spark DataFrame, используя Java. Теперь, как выполнить разделение, используя java? Для scala это объясняется здесь: Spark Scala: Как преобразовать Dataframe [вектор] в DataFrame [f1: Double, ..., fn: Double)] , но я не могу чтобы найти способ действовать таким же образом в java, как указано в ссылке.

Схема для информационного кадра приведена ниже, и значение IndexedUserID должно быть перенесено во вновь созданный столбец рекомендаций:

root
 |-- IndexedArtistID: integer (nullable = false)
 |-- recommendations: array (nullable = true)
 |    |-- element: struct (containsNull = true)
 |    |    |-- IndexedUserID: integer (nullable = true)
 |    |    |-- rating: float (nullable = true)

1 Ответ

0 голосов
/ 20 января 2020

Я попытался найти решение вопроса, и я должен сказать, что есть много контента, доступного для проблем, с которыми сталкиваются люди в python и scala для искры, но очень мало контента доступно в java. Таким образом, решение будет go следующим образом:

List<ElementStruct> structElements = dataFrameWithFeatures.javaRDD().map(row -> {
        int artistId = row.getInt(0);
        List<Object> recommendations = row.getList(1);
        return new ElementStruct(artistId, recommendations);
    }).collect();

    List<Recommendation> recommendations = new ArrayList<>();
    for (ElementStruct element : structElements) {
        List<Object> features = element.getFeatures();
        int artistId = element.getArtistId();
        for (int i = 0; i < features.size(); i++) {
            Object o = ((GenericRowWithSchema) features.get(i)).get(0);
            recommendations.add(new Recommendation(artistId, (int) o));
        }
    }
    SparkSession sparkSession = SessionCreator.getOrCreateSparkSession();
    Dataset<Row> decomposedDataframe = sparkSession.createDataFrame(recommendations, Recommendation.class);

Класс ElementStruct

import java.io.Serializable;
import java.util.List;

public class ElementStruct implements Serializable {
    private int artistId;
    private List<Object> features;

    public ElementStruct(int artistId, List<Object> features) {
        this.artistId = artistId;
        this.features = features;
    }

    public int getArtistId() {
        return artistId;
    }

    public void setArtistId(int artistId) {
        this.artistId = artistId;
    }

    public List<Object> getFeatures() {
        return features;
    }

    public void setFeatures(List<Object> features) {
        this.features = features;
    }
}

Класс рекомендаций

import java.io.Serializable;

public class Recommendation implements Serializable {
    private int artistId;
    private int userId;

    public Recommendation(int artistId, int userId){
        this.artistId = artistId;
        this.userId = userId;
    }

    public int getArtistId() {
        return artistId;
    }

    public void setArtistId(int artistId) {
        this.artistId = artistId;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }
}

Объяснение: 1. Для каждой строки в кадре данных получить художника и функцию в виде списка, чтобы облегчить дальнейшую обработку. Сохраните художника и список функций в виде объекта java (в данном случае это структура элементов).

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

Наконец, создайте фрейм данных из этого списка объектов, полученных на 2-м шаге.

Результат:

root
 |-- artistId: integer (nullable = false)
 |-- userId: integer (nullable = false)

+---------------+----------------+
|       artistId|          userId|
+---------------+----------------+
|           1580|919             |
|           1580|0.00249262      |
|           4900|41749           |
|           4900|7.143963        |
|           5300|0               |
|           5300|2.0147272E-4    |
|           6620|208780          |
|           6620|9.81092         |
+---------------+----------------+
...