Dataframe 如何在PySpark数据帧中加载大的双精度数字,并将其保留回去,而不将数字格式更改为科学符号或精度?
我有这样一个CSV:Dataframe 如何在PySpark数据帧中加载大的双精度数字,并将其保留回去,而不将数字格式更改为科学符号或精度?,dataframe,pyspark,user-defined-functions,pyspark-dataframes,scientific-notation,Dataframe,Pyspark,User Defined Functions,Pyspark Dataframes,Scientific Notation,我有这样一个CSV: COL,VAL TEST,100000000.12345679 TEST2,200000000.1234 TEST3,9999.1234679123 我想将列VAL作为数字类型加载(由于项目的其他要求),然后按照以下结构将其保存回另一个CSV: +-----+------------------+ | COL| VAL| +-----+------------------+ | TEST|100000000.12345679| |TEST2|
COL,VAL
TEST,100000000.12345679
TEST2,200000000.1234
TEST3,9999.1234679123
我想将列VAL
作为数字类型加载(由于项目的其他要求),然后按照以下结构将其保存回另一个CSV:
+-----+------------------+
| COL| VAL|
+-----+------------------+
| TEST|100000000.12345679|
|TEST2| 200000000.1234|
|TEST3| 9999.1234679123|
+-----+------------------+
我面临的问题是,每当我加载它时,数字就变成了科学符号,我无法在不通知数据的精度和比例的情况下将其保留下来(我想使用文件中已经存在的数字,不管它是什么-我无法推断它)。
以下是我尝试过的:
用DoubleType()加载它
它给了我科学的符号:
schema = StructType([
StructField('COL', StringType()),
StructField('VAL', DoubleType())
])
csv_file = "Downloads/test.csv"
df2 = (spark.read.format("csv")
.option("sep",",")
.option("header", "true")
.schema(schema)
.load(csv_file))
df2.show()
+-----+--------------------+
| COL| VAL|
+-----+--------------------+
| TEST|1.0000000012345679E8|
|TEST2| 2.000000001234E8|
|TEST3| 9999.1234679123|
+-----+--------------------+
使用DecimalType()
加载它时,我需要指定精度
和比例
,否则,我会丢失点后的小数。但是,如果指定它,除了可能得不到正确的值(因为我的数据可能会四舍五入)之外,我会在点后得到零:
例如,使用:StructField('VAL',DecimalType(38,18))
I获得:
[Row(COL='TEST', VAL=Decimal('100000000.123456790000000000')),
Row(COL='TEST2', VAL=Decimal('200000000.123400000000000000')),
Row(COL='TEST3', VAL=Decimal('9999.123467912300000000'))]
要意识到,在这种情况下,我在右侧有零,而我不希望在新文件中有零
我发现解决这个问题的唯一方法是使用UDF
,我首先使用float()
删除科学符号,然后将其转换为字符串,以确保它能够按照我的需要持久化:
to_decimal = udf(lambda n: str(float(n)))
df2 = df2.select("*", to_decimal("VAL").alias("VAL2"))
df2 = df2.select(["COL", "VAL2"]).withColumnRenamed("VAL2", "VAL")
df2.show()
display(df2.schema)
+-----+------------------+
| COL| VAL|
+-----+------------------+
| TEST|100000000.12345679|
|TEST2| 200000000.1234|
|TEST3| 9999.1234679123|
+-----+------------------+
StructType(List(StructField(COL,StringType,true),StructField(VAL,StringType,true)))
有没有办法不使用UDF
技巧就达到同样的效果
谢谢大家! 您可以使用spark对sql查询执行此操作:
import org.apache.spark.SparkConf
import org.apache.spark.sql.{DataFrame, SparkSession}
val sparkConf: SparkConf = new SparkConf(true)
.setAppName(this.getClass.getName)
.setMaster("local[*]")
implicit val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
val df = spark.read.option("header", "true").format("csv").load(csv_file)
df.createOrReplaceTempView("table")
val query = "Select cast(VAL as BigDecimal) as VAL, COL from table"
val result = spark.sql(query)
result.show()
result.coalesce(1).write.option("header", "true").mode("overwrite").csv(outputPath + table)
我找到的最好的解决办法是大声吼叫。它仍在使用UDF
,但现在,它没有使用字符串来避免使用科学符号的变通方法。我现在还不能给出正确的答案,因为我仍然希望有人能提供一个没有UDF的解决方案(或者很好地解释为什么没有UDF
s它是不可能的)
CSV:
应用默认PySparkDecimalType
精度和比例加载CSV:
输出:
+-----+----------------------------+
|COL |VAL |
+-----+----------------------------+
|TEST |100000000.123456790000000000|
|TEST2|200000000.123400000000000000|
|TEST3|9999.123467912300000000 |
|TEST4|123456789.012345670000000000|
+-----+----------------------------+
+-----+------------------+
|COL |VAL |
+-----+------------------+
|TEST |100000000.12345679|
|TEST2|200000000.1234 |
|TEST3|9999.1234679123 |
|TEST4|123456789.01234567|
+-----+------------------+
准备报告时(打印或保存在新文件中),可将格式应用于尾随零:
输出:
+-----+----------------------------+
|COL |VAL |
+-----+----------------------------+
|TEST |100000000.123456790000000000|
|TEST2|200000000.123400000000000000|
|TEST3|9999.123467912300000000 |
|TEST4|123456789.012345670000000000|
+-----+----------------------------+
+-----+------------------+
|COL |VAL |
+-----+------------------+
|TEST |100000000.12345679|
|TEST2|200000000.1234 |
|TEST3|9999.1234679123 |
|TEST4|123456789.01234567|
+-----+------------------+
你好@sofiane belghali,谢谢,但没用。我将您的代码转换为PySpark(Python),并将BigDecimal
更改为Decimal
(PySpark没有第一个),结果显示为DecimalType(10,0)
。它删除了点后的小数。我刚刚发现我可以使用decimal.decimal(100000000.1234567900000000.normalize()
)更接近我想要的值,但是我在DecimalType()
中没有找到normalize()
方法。有什么想法吗?
+-----+------------------+
|COL |VAL |
+-----+------------------+
|TEST |100000000.12345679|
|TEST2|200000000.1234 |
|TEST3|9999.1234679123 |
|TEST4|123456789.01234567|
+-----+------------------+