Dataframe 如何在PySpark数据帧中加载大的双精度数字,并将其保留回去,而不将数字格式更改为科学符号或精度?

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|

我有这样一个CSV:

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:
  • 应用默认PySpark
    DecimalType
    精度和比例加载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|
    +-----+------------------+