Чтобы предложить лучшее решение, я могу сказать, что обнаружил следующий метод:
parseFloat((0.1 + 0.2).toFixed(10)) => Will return 0.3
Позвольте мне объяснить, почему это лучшее решение. Как упоминалось выше в других ответах, рекомендуется использовать готовые для использования функции Javascript toFixed () для решения проблемы. Но, скорее всего, вы столкнетесь с некоторыми проблемами.
Представьте, что вы собираетесь добавить два числа с плавающей запятой, такие как 0.2
и 0.7
, вот оно: 0.2 + 0.7 = 0.8999999999999999
.
Ваш ожидаемый результат 0.9
означает, что в этом случае вам нужен результат с точностью до 1 цифры. Поэтому вы должны были использовать (0.2 + 0.7).tofixed(1)
, но вы не можете просто указать определенный параметр toFixed (), поскольку он зависит от заданного числа, например
`0.22 + 0.7 = 0.9199999999999999`
. В этом примере вам нужна точность в 2 цифры так что это должно быть toFixed(2)
, так что должно быть параметром для каждого заданного числа с плавающей запятой?
Вы могли бы сказать, что пусть это будет 10 в каждой ситуации:
(0.2 + 0.7).toFixed(10) => Result will be "0.9000000000"
Черт! Что вы собираетесь делать с этими нежелательными нулями после 9? Пришло время преобразовать его в float, чтобы сделать его по вашему желанию:
parseFloat((0.2 + 0.7).toFixed(10)) => Result will be 0.9
Теперь, когда вы нашли решение, лучше предложить его как функцию:
function floatify(number){
return parseFloat((number).toFixed(10));
}
function addUp(){
var number1 = +$("#number1").val();
var number2 = +$("#number2").val();
var unexpectedResult = number1 + number2;
var expectedResult = floatify(number1 + number2);
$("#unexpectedResult").text(unexpectedResult);
$("#expectedResult").text(expectedResult);
}
addUp();
input{
width: 50px;
}
#expectedResult{
color: green;
}
#unexpectedResult{
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="number1" value="0.2" onclick="addUp()" onkeyup="addUp()"/> +
<input id="number2" value="0.7" onclick="addUp()" onkeyup="addUp()"/> =
<p>Expected Result: <span id="expectedResult"></span></p>
<p>Unexpected Result: <span id="unexpectedResult"></span></p>
Вы можете использовать его следующим образом:
var x = 0.2 + 0.7;
floatify(x); => Result: 0.9
Ну, вы можете написать UDF, но почему бы вам? Существует уже немало инструментов, предназначенных для обработки этой категории задач:
from pyspark.sql import Row
from pyspark.ml.linalg import DenseVector
row = Row("gender", "foo", "bar")
df = sc.parallelize([
row("0", 3.0, DenseVector([0, 2.1, 1.0])),
row("1", 1.0, DenseVector([0, 1.1, 1.0])),
row("1", -1.0, DenseVector([0, 3.4, 0.0])),
row("0", -3.0, DenseVector([0, 4.1, 0.0]))
]).toDF()
Прежде всего StringIndexer
.
from pyspark.ml.feature import StringIndexer
indexer = StringIndexer(inputCol="gender", outputCol="gender_numeric").fit(df)
indexed_df = indexer.transform(df)
indexed_df.drop("bar").show()
## +------+----+--------------+
## |gender| foo|gender_numeric|
## +------+----+--------------+
## | 0| 3.0| 0.0|
## | 1| 1.0| 1.0|
## | 1|-1.0| 1.0|
## | 0|-3.0| 0.0|
## +------+----+--------------+
Далее OneHotEncoder
:
from pyspark.ml.feature import OneHotEncoder
encoder = OneHotEncoder(inputCol="gender_numeric", outputCol="gender_vector")
encoded_df = encoder.transform(indexed_df)
encoded_df.drop("bar").show()
## +------+----+--------------+-------------+
## |gender| foo|gender_numeric|gender_vector|
## +------+----+--------------+-------------+
## | 0| 3.0| 0.0|(1,[0],[1.0])|
## | 1| 1.0| 1.0| (1,[],[])|
## | 1|-1.0| 1.0| (1,[],[])|
## | 0|-3.0| 0.0|(1,[0],[1.0])|
## +------+----+--------------+-------------+
VectorAssembler
:
from pyspark.ml.feature import VectorAssembler
assembler = VectorAssembler(
inputCols=["gender_vector", "bar", "foo"], outputCol="features")
encoded_df_with_indexed_bar = (vector_indexer
.fit(encoded_df)
.transform(encoded_df))
final_df = assembler.transform(encoded_df)
Если bar
содержит категориальные переменные, вы можете использовать VectorIndexer
для установки необходимых метаданных:
from pyspark.ml.feature import VectorIndexer
vector_indexer = VectorIndexer(inputCol="bar", outputCol="bar_indexed")
, но это не так.
Наконец, вы можете обернуть все это с помощью конвейеров:
from pyspark.ml import Pipeline
pipeline = Pipeline(stages=[indexer, encoder, vector_indexer, assembler])
model = pipeline.fit(df)
transformed = model.transform(df)
Возможно, это очень надежный и чистый подход, чем запись всего с нуля. Есть некоторые предостережения, особенно если вам требуется последовательное кодирование между различными наборами данных. Вы можете прочитать больше в официальной документации для StringIndexer
и VectorIndexer
.
Другой способ получить сопоставимый выход - RFormula
, который :
blockquote>
RFormula
создает векторный столбец функций и двойной или строковый столбец метки. Подобно тому, как формулы используются в R для линейной регрессии, столбцы ввода строки будут однострочно закодированы, а числовые столбцы будут приведены к удвоению. Если столбец меток имеет строку типа, он будет сначала преобразован в double сStringIndexer
. Если столбец меток не существует в DataFrame, столбец меток вывода будет создан из указанной переменной ответа в формуле.from pyspark.ml.feature import RFormula rf = RFormula(formula="~ gender + bar + foo - 1") final_df_rf = rf.fit(df).transform(df)
Как вы можете видеть, это намного более кратким , но сложнее составить не позволяет многое настраивать. Тем не менее результат для простого конвейера, подобного этому, будет идентичным:
final_df_rf.select("features").show(4, False) ## +----------------------+ ## |features | ## +----------------------+ ## |[1.0,0.0,2.1,1.0,3.0] | ## |[0.0,0.0,1.1,1.0,1.0] | ## |(5,[2,4],[3.4,-1.0]) | ## |[1.0,0.0,4.1,0.0,-3.0]| ## +----------------------+ final_df.select("features").show(4, False) ## +----------------------+ ## |features | ## +----------------------+ ## |[1.0,0.0,2.1,1.0,3.0] | ## |[0.0,0.0,1.1,1.0,1.0] | ## |(5,[2,4],[3.4,-1.0]) | ## |[1.0,0.0,4.1,0.0,-3.0]| ## +----------------------+
Что касается ваших вопросов:
сделать UDF с аналогичной функциональностью, которую я могу использовать в Spark SQL-запрос (или каким-то другим способом, я полагаю)
blockquote>Это просто UDF, как и любой другой. Убедитесь, что вы используете поддерживаемые типы, и кроме того, все должно работать нормально.
взять RDD, полученную на карте, описанной выше, и добавить ее как новый столбец в dataframe user_data?
blockquote>from pyspark.ml.linalg import VectorUDT from pyspark.sql.types import StructType, StructField schema = StructType([StructField("features", VectorUDT(), True)]) row = Row("features") result.map(lambda x: row(DenseVector(x))).toDF(schema)
Примечание:
Для Spark 1.x замените
pyspark.ml.linalg
наpyspark.mllib.linalg
.