Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/340.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 由UDF更改为字符串的所有列的Pyspark数据帧数据类型_Python_Pyspark_Apache Spark Sql_Spark Dataframe_User Defined Functions - Fatal编程技术网

Python 由UDF更改为字符串的所有列的Pyspark数据帧数据类型

Python 由UDF更改为字符串的所有列的Pyspark数据帧数据类型,python,pyspark,apache-spark-sql,spark-dataframe,user-defined-functions,Python,Pyspark,Apache Spark Sql,Spark Dataframe,User Defined Functions,我有一个数据框,它有几个列,如email_address(字符串)、paid(整数)和date(日期时间类型) 我正在运行一个UDF,如下所述: from pyspark.sql.functions import udf, col def conv(column): date_format='%m/%d/%Y' a = None if column: try: a= datetime.strptime(str(column),'%

我有一个数据框,它有几个列,如email_address(字符串)、paid(整数)和date(日期时间类型)

我正在运行一个UDF,如下所述:

from pyspark.sql.functions import udf, col

def conv(column):
    date_format='%m/%d/%Y'
    a = None
    if column:
        try:
            a= datetime.strptime(str(column),'%Y-%m-%d').strftime(date_format)
            print("Inside Try")
        except:
            a = column
            print("Inside except")
    return a

conv_func = udf(conv)

df_new = date_df.select(*(conv_func(col(c)).alias(c) for c in date_df.columns))
因此,在df_new中,我希望电子邮件地址(字符串)、已付(整数)和日期(字符串)的格式从YYYY-MM-DD更改为MM/DD/YYYY

问题是UDF确实转换了格式,但也将付费列的数据类型从整数更改为字符串,这是我没有预料到的


我想知道如何避免使用这个UDF。

这里有一种方法可以用您正在尝试的方式来做到这一点

使用函数测试列是否为日期,并仅对这些列应用转换

from pyspark.sql.functions import udf, col
from pyspark.sql.types import BooleanType

def conv(column):
    date_format='%m/%d/%Y'
    a = datetime.strptime(str(column),'%Y-%m-%d').strftime(date_format)
    return a

def is_date(column):
    try:
        conv(column)
        return True
    except:
        return False

conv_func = udf(conv)
date_udf = udf(is_date, BooleanType())

df_new = date_df.select(
    *(conv_func(col(c)).alias(c) if date_udf(col(c)) else col(c) for c in date_df.columns)
)

我还没有测试过这段(伪)代码(如果您提供了一个,它会有所帮助),但我认为类似的方法应该可以工作。

这里有一种方法可以用您正在尝试的方式来实现这一点

使用函数测试列是否为日期,并仅对这些列应用转换

from pyspark.sql.functions import udf, col
from pyspark.sql.types import BooleanType

def conv(column):
    date_format='%m/%d/%Y'
    a = datetime.strptime(str(column),'%Y-%m-%d').strftime(date_format)
    return a

def is_date(column):
    try:
        conv(column)
        return True
    except:
        return False

conv_func = udf(conv)
date_udf = udf(is_date, BooleanType())

df_new = date_df.select(
    *(conv_func(col(c)).alias(c) if date_udf(col(c)) else col(c) for c in date_df.columns)
)

我还没有测试过这段(伪)代码(如果您提供了,它会有所帮助),但我认为类似的方法应该可以工作。

要更改日期列的格式,可以使用pyspark sql函数中的date_格式。我创建了示例数据并进行了测试

 >>> for pyspark.sql import functions as F 
 >>> l=[('2018-01-22','id1',123,'2018-01-21'),('2018-01-22','id2',234,'2018-01-21'),('2018-01-22','id3',345,'2018-01-21'),('2018-01-22','id2',456,'2018-01-21')]
 >>> df = spark.createDataFrame(l,['date1','id','value','date2'])
 >>> df = df.select(df.date1.cast('date'),'id','value',df.date2.cast('date'))
 >>> df.printSchema()
 root
  |-- date1: date (nullable = true)
  |-- id: string (nullable = true)
  |-- value: long (nullable = true)
  |-- date2: date (nullable = true)

 >>> df.show()
 +----------+---+-----+----------+
 |     date1| id|value|     date2|
 +----------+---+-----+----------+
 |2018-01-22|id1|  123|2018-01-21|
 |2018-01-22|id2|  234|2018-01-21|
 |2018-01-22|id3|  345|2018-01-21|
 |2018-01-22|id2|  456|2018-01-21|
 +----------+---+-----+----------+
 >>> dcols,cols = [],[]
 >>> for x in df.schema.fields:
 ...     if repr(x.dataType) == 'DateType':
 ...        dcols.append(x.name)
 ...     else:
 ...        cols.append(x.name)
 ...
 >>> dcols
 ['date1', 'date2']
 >>> cols
 ['id', 'value']
 >>> df.select([F.date_format(c,'MM/dd/yyy').alias('%s'%c) for c in dcols]+cols).show()
 +----------+----------+---+-----+
 |     date1|     date2| id|value|
 +----------+----------+---+-----+
 |01/22/2018|01/21/2018|id1|  123|
 |01/22/2018|01/21/2018|id2|  234|
 |01/22/2018|01/21/2018|id3|  345|
 |01/22/2018|01/21/2018|id2|  456|
 +----------+----------+---+-----+

 ## If you still want to use UDF

 >>> from datetime import datetime
 >>> def conv(column):
 ...     date_format='%m/%d/%Y'
 ...     a = datetime.strptime(str(column),'%Y-%m-%d').strftime(date_format)
 ...     return a
 ...
 >>> conv_func = F.udf(conv)
 >>> df.select([conv_func(F.col(x)).alias('%s'%x) for x in dcols]+cols).show()
 +----------+----------+---+-----+
 |     date1|     date2| id|value|
 +----------+----------+---+-----+
 |01/22/2018|01/21/2018|id1|  123|
 |01/22/2018|01/21/2018|id2|  234|
 |01/22/2018|01/21/2018|id3|  345|
 |01/22/2018|01/21/2018|id2|  456|
 +----------+----------+---+-----+

希望这会有所帮助。

要更改日期列的格式,可以使用pyspark sql函数中的date_格式。我创建了示例数据并进行了测试

 >>> for pyspark.sql import functions as F 
 >>> l=[('2018-01-22','id1',123,'2018-01-21'),('2018-01-22','id2',234,'2018-01-21'),('2018-01-22','id3',345,'2018-01-21'),('2018-01-22','id2',456,'2018-01-21')]
 >>> df = spark.createDataFrame(l,['date1','id','value','date2'])
 >>> df = df.select(df.date1.cast('date'),'id','value',df.date2.cast('date'))
 >>> df.printSchema()
 root
  |-- date1: date (nullable = true)
  |-- id: string (nullable = true)
  |-- value: long (nullable = true)
  |-- date2: date (nullable = true)

 >>> df.show()
 +----------+---+-----+----------+
 |     date1| id|value|     date2|
 +----------+---+-----+----------+
 |2018-01-22|id1|  123|2018-01-21|
 |2018-01-22|id2|  234|2018-01-21|
 |2018-01-22|id3|  345|2018-01-21|
 |2018-01-22|id2|  456|2018-01-21|
 +----------+---+-----+----------+
 >>> dcols,cols = [],[]
 >>> for x in df.schema.fields:
 ...     if repr(x.dataType) == 'DateType':
 ...        dcols.append(x.name)
 ...     else:
 ...        cols.append(x.name)
 ...
 >>> dcols
 ['date1', 'date2']
 >>> cols
 ['id', 'value']
 >>> df.select([F.date_format(c,'MM/dd/yyy').alias('%s'%c) for c in dcols]+cols).show()
 +----------+----------+---+-----+
 |     date1|     date2| id|value|
 +----------+----------+---+-----+
 |01/22/2018|01/21/2018|id1|  123|
 |01/22/2018|01/21/2018|id2|  234|
 |01/22/2018|01/21/2018|id3|  345|
 |01/22/2018|01/21/2018|id2|  456|
 +----------+----------+---+-----+

 ## If you still want to use UDF

 >>> from datetime import datetime
 >>> def conv(column):
 ...     date_format='%m/%d/%Y'
 ...     a = datetime.strptime(str(column),'%Y-%m-%d').strftime(date_format)
 ...     return a
 ...
 >>> conv_func = F.udf(conv)
 >>> df.select([conv_func(F.col(x)).alias('%s'%x) for x in dcols]+cols).show()
 +----------+----------+---+-----+
 |     date1|     date2| id|value|
 +----------+----------+---+-----+
 |01/22/2018|01/21/2018|id1|  123|
 |01/22/2018|01/21/2018|id2|  234|
 |01/22/2018|01/21/2018|id3|  345|
 |01/22/2018|01/21/2018|id2|  456|
 +----------+----------+---+-----+

希望这有帮助。

为什么pyspark.sql.types中的
会导入StringType
,您的
udf
类型在哪里?例如,
udf(conv,StringType())
因为我不希望类型严格为StringType,所以我没有提到udf类型。正如您可能已经注意到的,dataframe也有整数类型,我不想将该列强制转换为字符串。我已删除了未使用的导入。不幸的是,udf必须有一个类型,默认情况下,如果您不指定它,它将是“StringType”,这没有任何意义。为什么要对电子邮件应用日期格式。只需对实际要转换的列使用
with column
。不要使用
udf
。SQL函数很容易做到这一点。为什么pyspark.SQL.types中的
会导入StringType
,而您的
udf
类型在哪里?例如,
udf(conv,StringType())
因为我不希望类型严格为StringType,所以我没有提到udf类型。正如您可能已经注意到的,dataframe也有整数类型,我不想将该列强制转换为字符串。我已删除了未使用的导入。不幸的是,udf必须有一个类型,默认情况下,如果您不指定它,它将是“StringType”,这没有任何意义。为什么要对电子邮件应用日期格式。只需对实际要转换的列使用
with column
。不要使用
udf
。SQL函数可以轻松做到这一点。