Как преобразовать тип Any List в тип Double (Scala)

#scala #apache-spark #mean #databricks

#scala #apache-spark #имеется в виду #databricks

Вопрос:

Я новичок в Scala, и я хотел бы понять некоторые базовые вещи.

Прежде всего, мне нужно вычислить среднее значение для определенного столбца фрейма данных и использовать результат в качестве переменной типа double.

После некоторых интернет-исследований я смог вычислить среднее значение и одновременно передать его в список типа Any с помощью следующей команды:

 val avgX_List = mainDataFrame.groupBy().agg(mean("_c1")).collect().map(_(0)).toList
  

где «_c1» — это второй столбец моего фрейма данных. Эта строка кода возвращает список с типом List[Any].

Чтобы передать результат в переменную, я использовал следующую команду:

 var avgX = avgX_List(0)
  

надеялся, что var avgX автоматически примет тип double, но этого, очевидно, не произошло.

Итак, теперь давайте начнем задавать вопросы:

  1. Что делает map(_(0)) do ? Я знаю основное определение map() преобразования, но я не могу найти объяснение с этим точным аргументом

  2. Я знаю, что при использовании .toList метода в конце команды моим результатом будет список с типом Any . Есть ли способ, которым я мог бы изменить это на List, который содержит элементы типа Double ? Или даже преобразовать этот

  3. Считаете ли вы, что было бы гораздо уместнее передать столбец моего фрейма данных в список [Double], а затем вычислить среднее значение его элементов?

  4. Является ли решение, которое я показал выше, правильным с любой точки зрения, исходя из моей проблемы? Я знаю, что «это работает» отличается от «правильного решения»?

Подводя итог, мне нужно вычислить среднее значение определенного столбца фрейма данных и получить результат в виде переменной типа double.

Обратите внимание, что: я грек, и мне иногда трудно понять некоторые английские кодирующие «сленги».

Ответ №1:

map(_(0)) это ярлык для map( (r: Row) => r(0) ) , который, в свою очередь, является ярлыком для map( (r: Row) => r.apply(0) ) . apply Метод возвращает Any , и поэтому вы теряете правильный тип. Попробуйте вместо этого использовать map(_.getAs[Double](0)) или map(_.getDouble(0)) .

Сбор всех записей столбца и последующее вычисление среднего значения было бы крайне контрпродуктивным, потому что вам пришлось бы отправлять огромные объемы данных на главный узел, а затем выполнять все вычисления на этом единственном центральном узле. Это было бы полной противоположностью тому, для чего подходит Spark.

Вам также не нужно collect(...).toList , потому что вы можете получить доступ к 0-й записи напрямую (не имеет значения, получаете ли вы ее из Array или из a List ). Так как вы все Row равно сворачиваете все в один, вы могли бы полностью избавиться от map шага, немного изменив порядок методов:

 val avgX = mainDataFrame.groupBy().agg(mean("_c1")).collect()(0).getDouble(0)
  

Его можно записать еще короче, используя first метод:

 val avgX = mainDataFrame.groupBy().agg(mean("_c1")).first().getDouble(0)
  

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

1. Спасибо за ваш быстрый ответ. Итак, вы думаете, что решение, которое я нашел, эффективно, исходя из результата, которого я хочу достичь? Кроме того, когда вы говорите, что collectAsList уже существует, вы имеете в виду, что он существует в .toList части команды? Наконец, использование map(_.getAs[Double](0)) выдает мне эту ошибку: error: missing parameter type for expanded function ((x$2) => x$2.getAs[Double](0)) . Вы хоть представляете, что это значит?

2. @ArisKantas Ах, нет, извините, забудьте collectAsList часть, это вопрос совместимости с Java, на самом деле он возвращает java.util. Список, вероятно, не то, что вы хотите. map(_.getDouble(0)) Работает или что случилось с ошибкой, о которой вы только что сообщили?

3. Последнее предложение вашего ответа сработало! Мне нужно изучить гораздо больше, чтобы понять, как именно работают эти методы. Особенно map(). Не могли бы вы кратко объяснить мне, почему map() не нужен в этом случае? Спасибо от имени!

Ответ №2:

 #Any dataType in Scala can't be directly converted to Double.
#Use toString amp; then toDouble on final captured result.

#Eg-

#scala> x
#res22: Any = 1.0

#scala> x.toString.toDouble
#res23: Double = 1.0

#Note- Instead of using map().toList() directly use (0)(0) to get the final value from your resultset.


#TestSample(Scala)-

val wa = Array("one","two","two")
val wrdd = sc.parallelize(wa,3).map(x=>(x,1))
val wdf = wrdd.toDF("col1","col2")
val x = wdf.groupBy().agg(mean("col2")).collect()(0)(0).toString.toDouble

#O/p-
#scala> val x = wdf.groupBy().agg(mean("col2")).collect()(0)(0).toString.toDouble
#x: Double = 1.0

  

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

1. Где вы видели collect()(0)(0) в моем коде? Это то же самое, что collect()(0).apply(0) , поэтому он теряет всю информацию о типе и возвращает Any . Не то чтобы я утверждал что-то другое…

2. Это мой пример кода. Я просто говорю, что getDouble() недоступен для переменной любого типа данных.

3. Он доступен на org.apache.spark.sql.Row . Опять же, я никогда не утверждал, что это getDouble() должно быть вызвано на Any . Ваш ответ пытается решить проблему, которой нет.

4. Я проверил это! он работает нормально 🙂 val wa = Array("one","two","two") val wrdd = sc.parallelize(wa,3).map(x=>(x,1)) val wdf = wrdd.toDF("col1","col2") wdf.groupBy().agg(mean("col2")).collect()(0).getDouble(0)

5. Здравствуйте! Спасибо за ваш ответ. Не могли бы вы объяснить мне, что collect()(0)(0) делает? Также, если я хочу преобразовать фрейм данных в список, что вы предлагаете, чтобы я мог использовать правильный список типов (т. Е. Double)? мой подход таков: var trainingCoordList = trainingCoordDataFrame.collect().toList но он возвращает тип, List[org.apache.spark.sql.Row] который я точно не знаю, что это такое!