Unable to infer schema when loading Parquet file - apache-spark
response = "mi_or_chd_5"
outcome = sqlc.sql("""select eid,{response} as response
from outcomes
where {response} IS NOT NULL""".format(response=response))
outcome.write.parquet(response, mode="overwrite") # Success
print outcome.schema
StructType(List(StructField(eid,IntegerType,true),StructField(response,ShortType,true)))
But then:
outcome2 = sqlc.read.parquet(response) # fail
fails with:
AnalysisException: u'Unable to infer schema for Parquet. It must be specified manually.;'
in
/usr/local/lib/python2.7/dist-packages/pyspark-2.1.0+hadoop2.7-py2.7.egg/pyspark/sql/utils.pyc in deco(*a, **kw)
The documentation for parquet says the format is self describing, and the full schema was available when the parquet file was saved. What gives?
Using Spark 2.1.1. Also fails in 2.2.0.
Found this bug report, but was fixed in
2.0.1, 2.1.0.
UPDATE: This work when on connected with master="local", and fails when connected to master="mysparkcluster".
This error usually occurs when you try to read an empty directory as parquet.
Probably your outcome Dataframe is empty.
You could check if the DataFrame is empty with outcome.rdd.isEmpty() before writing it.
In my case, the error occured because I was trying to read a parquet file which started with an underscore (e.g. _lots_of_data.parquet). Not sure why this was an issue, but removing the leading underscore solved the problem.
See also:
Re: Spark-2.0.0 fails reading a parquet dataset generated by Spark-1.6.2
I'm using AWS Glue and I received this error while reading data from a data catalog table (location: s3 bucket).
After bit of analysis I realised that, this is due to file not available in file location(in my case s3 bucket path).
Glue was trying to apply data catalog table schema on a file which doesn't exist.
After copying file into s3 bucket file location, issue got resolved.
Hope this helps someone who encounters/encountered an error in AWS Glue.
This case occurs when you try to read a table that is empty. If the table had correctly inserted data, there should be no problem.
Besides that with parquet, the same thing happens with ORC.
I see there are already so many Answers. But the issue I faced was my Spark job was trying to read a file which is being overwritten by another Spark job that was previously started. It sounds bad, but I did that mistake.
Just to emphasize #Davos answer in a comment, you will encounter this exact exception error, if your file name has a dot . or an underscore _ at start of the filename
val df = spark.read.format("csv").option("delimiter", "|").option("header", "false")
.load("/Users/myuser/_HEADER_0")
org.apache.spark.sql.AnalysisException:
Unable to infer schema for CSV. It must be specified manually.;
Solution is to rename the file and try again (e.g. _HEADER rename to HEADER)
Happened to me for a parquet file that was in the process of being written to.
Just need to wait for it to be completely written.
In my case, the error occurred because the filename contained underscores. Rewriting / reading the file without underscores (hyphens were OK) solved the problem...
For me this happened when I thought loading the correct file path but instead pointed a incorrect folder
I ran into a similar problem with reading a csv
spark.read.csv("s3a://bucket/spark/csv_dir/.")
gave an error of:
org.apache.spark.sql.AnalysisException: Unable to infer schema for CSV. It must be specified manually.;
I found if I removed the trailing . and then it works. ie:
spark.read.csv("s3a://bucket/spark/csv_dir/")
I tested this for parquet adding a trailing . and you get an error of:
org.apache.spark.sql.AnalysisException: Unable to infer schema for Parquet. It must be specified manually.;
I just encountered the same problem but none of the solutions here work for me. I try to merge the row groups of my parquet files on hdfs by first reading them and write it to another place using:
df = spark.read.parquet('somewhere')
df.write.parquet('somewhere else')
But later when I query it with
spark.sql('SELECT sth FROM parquet.`hdfs://host:port/parquetfolder/` WHERE .. ')
It shows the same problem.
I finally solve this by using pyarrow:
df = spark.read.parquet('somewhere')
pdf = df.toPandas()
adf = pa.Table.from_pandas(pdf) # import pyarrow as pa
fs = pa.hdfs.connect()
fw = fs.open(path, 'wb')
pq.write_table(adf, fw) # import pyarrow.parquet as pq
fw.close()
Check if .parquet files available at the response path. I am assuming, either files don't exist or it may be exist in some internal(partitioned) folders. If files are available under multiple hierarchy folders then append /* for each folder.
As in my case .parquet files were under 3 folders from base_path, so I have given path as base_path/*/*/*
As others mentioned, in my case this error appeared when I was reading S3 keys that did not exist.
A solution is filter-in keys that do exist:
import com.amazonaws.services.s3.AmazonS3URI
import org.apache.hadoop.fs.{FileSystem, Path}
import org.apache.spark.sql.SparkSession
import java.net.URI
def addEndpointToUrl(url: String, domain: String = "s3.amazonaws.com"): String = {
val uri = new URI(url)
val hostWithEndpoint = uri.getHost + "." + domain
new URI(uri.getScheme, uri.getUserInfo, hostWithEndpoint, uri.getPort, uri.getPath, uri.getQuery, uri.getFragment).toString
}
def createS3URI(url: String): AmazonS3URI = {
try {
// try to instantiate AmazonS3URI with url
new AmazonS3URI(url)
} catch {
case e: IllegalArgumentException if e.getMessage.
startsWith("Invalid S3 URI: hostname does not appear to be a valid S3 endpoint") => {
new AmazonS3URI(addEndpointToUrl(url))
}
}
}
def s3FileExists(spark: SparkSession, url: String): Boolean = {
val amazonS3Uri: AmazonS3URI = createS3URI(url)
val s3BucketUri = new URI(s"${amazonS3Uri.getURI().getScheme}://${amazonS3Uri.getBucket}")
FileSystem
.get(s3BucketUri, spark.sparkContext.hadoopConfiguration)
.exists(new Path(url))
}
and you can use it as:
val partitions = List(yesterday, today, tomorrow)
.map(f => somepath + "/date=" + f)
.filter(f => s3FileExists(spark, f))
val df = spark.read.parquet(partitions: _*)
For that solution I took some code out of spark-redshift project, here.
You are just loading a parquet file , Of course parquet had valid
schema. Otherwise it would not been saved as parquet. This error means
-
Either parquet file does not exist . (99.99% cases this is the issue. Spark error messages are often less obvious)
Somehow the parquet file got corrupted or Or It's not a parquet file at all
I ran into this issue because of folder in folder issue.
for example folderA.parquet was supposed to have partion.... but instead it have folderB.parquet which inside have partition.
Resolution,
transfer the file to parent folder and delete the subfolder.
you can read with /*
outcome2 = sqlc.read.parquet(f"{response}/*") # work for me
My problem was moving the files to another location and not changing the path in the files in the folder _spark_metadata.
To fix it do something like:
sed -i "s|/old/path|/new/path|g" ** .*
Seems this issue can be caused by a lot of reasons, I am providing another scenario:
By default the spark parquet source is using "partition inferring" which means it requires the file path to be partition in Key=Value pairs and the loads happens at the root.
To avoid this, if we assure all the leaf files have identical schema, then we can use
df = spark.read.format("parquet")\
.option("recursiveFileLookup", "true")
to disable the "partition inferring" manually. It basically load files one by one using the parquet's logical_type.
Related
Reading Parquet file with Pyspark returns java.lang.AssertionError: assertion failed: Conflicting directory structures detected. Suspicious paths
I am trying to load parquet files in the following directories: s3://dir1/model=m1/version=newest/versionnumber=3/scores/marketplace_id-1 s3://dir1/model=m1/version=newest/versionnumber=3/scores/marketplace_id-2 s3://dir1/model=m1/version=newest/versionnumber=3/scores/marketplace_id-3 s3://dir1/model=m1/version=newest/versionnumber=3/scores/marketplace_id-4 s3://dir1/model=m1/version=newest/versionnumber=3/scores/marketplace_id-5 s3://dir1/model=m1/version=newest/versionnumber=3/scores/marketplace_id-6 s3://dir1/model=m1/version=newest/versionnumber=3/scores/marketplace_id-7 s3://dir1/model=m1/version=newest/versionnumber=3/scores/marketplace_id-8 This is what I wrote in Pyspark s3_bucket_location_of_data = "s3://dir1/model=m1/version=newest/versionnumber=3/scores/" df = spark.read.parquet(s3_bucket_location_of_data) but I received the following error: Py4JJavaError: An error occurred while calling o109.parquet. : java.lang.AssertionError: assertion failed: Conflicting directory structures detected. Suspicious paths: s3://dir1/model=m1/version=newest/versionnumber=3/scores/marketplace_id-1 s3://dir1/model=m1/version=newest/versionnumber=3/scores/marketplace_id-2 s3://dir1/model=m1/version=newest/versionnumber=3/scores/marketplace_id-3 s3://dir1/model=m1/version=newest/versionnumber=3/scores/marketplace_id-4 s3://dir1/model=m1/version=newest/versionnumber=3/scores/marketplace_id-5 s3://dir1/model=m1/version=newest/versionnumber=3/scores/marketplace_id-6 s3://dir1/model=m1/version=newest/versionnumber=3/scores/marketplace_id-7 s3://dir1/model=m1/version=newest/versionnumber=3/scores/marketplace_id-8 After reading other StackOverflow posts like this, I tried the following: base_path="s3://dir1/" # I have tried to set this to "s3://dir1/model=m1/version=newest/versionnumber=3/scores/" as well, but it didn't work s3_bucket_location_of_data = "s3://dir1/model=m1/version=newest/versionnumber=3/scores/" df = spark.read.option("basePath", base_path).parquet(s3_bucket_location_of_data) but that returned a similar error message as above. I am new to Spark/Pyspark and I don't know what I could possibly be doing wrong here. Thank you in advance for your answers!
You don't need to specify the detailed path. Just load the files from the base_path. df = spark.read.parquet("s3://dir1") df.filter("model = 'm1' and version = 'newest' and versionnumber = 3") The directory structure is already partitioned by 3 columns, model, version and versionnumber. So read the base and filter the partition, then you could read all the parquet files under the partition path.
How to save files in same directory using saveAsNewAPIHadoopFile spark scala
I am using spark streaming and I want to save each batch of spark streaming on my local in Avro format. I have used saveAsNewAPIHadoopFile to save data in Avro format. This works well. But it overwrites the existing file. Next batch data will overwrite the old data. Is there any way to save Avro file in common directory? I tried by adding some properties of Hadoop job conf for adding a prefix in the file name. But not working any properties. dstream.foreachRDD { rdd.saveAsNewAPIHadoopFile( path, classOf[AvroKey[T]], classOf[NullWritable], classOf[AvroKeyOutputFormat[T]], job.getConfiguration() ) }
Try this - You can make your process split into 2 steps : Step-01 :- Write Avro file using saveAsNewAPIHadoopFile to <temp-path> Step-02 :- Move file from <temp-path> to <actual-target-path> This will definitely solve your problem for now. I will share my thoughts if I get to fulfill this scenario in one step instead of two. Hope this is helpful.
Spark (PySpark) File Already Exists Exception
I am trying to save a data frame as a text file, however, I am getting a File Already Exists exception. I tried adding the mode to the code but to no avail. Furthermore, the file does not actually exists. Would anyone have an idea how I can solve this problem? I am using PySpark This is the code: distFile = sc.textFile("/Users/jeremy/Downloads/sample2.nq") mapper = distFile.map(lambda q: __q2v(q)) reducer = mapper.reduceByKey(lambda a, b: a + os.linesep + b) data_frame = reducer.toDF(["context", "triples"]) data_frame.coalesce(1).write.partitionBy("context").text("/Users/jeremy/Desktop/so") May I add that the exception is being raised after some time and that some data is actually stored in temporary files (which are obviously deleted). Thanks! Edit: Exception can be found here: https://gist.github.com/jerdeb/c30f65dc632fb997af289dac4d40c743
you can used overwrite or append for replacing the file or adding the data into same file. data_frame.coalesce(1).write.mode('overwrite').partitionBy("context").text("/Users/jeremy/Desktop/so") or data_frame.coalesce(1).write.mode('append').partitionBy("context").text("/Users/jeremy/Desktop/so")
I had the same problem and was able get around it with this: outputDir = "/FileStore/tables/my_result/" dbutils.fs.rm(outputDir , True) Just change the outputDir variable to whatever directory you are writing to.
You should check your executors and look at the logs of the ones that are failing. In my case, I had a coalesce(1) on a large DF. 4 of my executors failed - 3 of them had the same error of org.apache.hadoop.fs.FileAlreadyExistsException: File already exists. However, 1 of them had a different exception: org.apache.spark.memory.SparkOutOfMemoryError: Unable to acquire 262144 bytes of memory, got 148328 I was able to fix it by increasing the executor memory so that the coalesce did not cause an out of memory error.
Spark - Cannot Write To Parquet File when using mapWithInputSplit
I am trying to solve a simple log parsing problem: given N log files logfile.20160601.txt logfile.29169692.txt ... I would save a parquet file with the date key of the log. In order to accomplish that I found this way in order to get the inputSplit path. val data = sc.textFile("/logdirectory/*.*") val logsWithFileName = data.mapPartitionsWithInputSplit { (inputSplit, iterator) => val file = inputSplit.asInstanceOf[FileSplit] val logDateKey = getDatekeyFromPath(file) iterator.map { tpl => ( logDateKey, tpl._2.toString) } } val logs = logsWithFileName.map(item => LogItem(item._1,item._2)) val df = logs.toDF Now I try to save the dataframe df.write.partitionBy("logdatekey", "hostname").save("C:/temp/results.parquet") but I receive this message Output directory file:/C:/temp/results.parquet already exists org.apache.hadoop.mapred.FileAlreadyExistsException: Output directory file:/C:/temp/results.parquet already exists at org.apache.hadoop.mapred.FileOutputFormat.checkOutputSpecs(FileOutputFormat.java:132) at org.apache.spark.rdd.PairRDDFunctions$$anonfun$saveAsHadoopDataset$1.apply$mcV$sp(PairRDDFunctions.scala:1179) Does anyone experimented this strange behavior? Could this be related to the use of input split? Many thanks in adavance Rob
Well you error message says allo. You are trying to write an output that already exist. You just need to what are the available save operations : Save operations can optionally take a SaveMode, that specifies how to handle existing data if present. It is important to realize that these save modes do not utilize any locking and are not atomic. Additionally, when performing a Overwrite, the data will be deleted before writing out the new data. SaveMode.ErrorIfExists (behavior by default) when saving a DataFrame to a data source, if data already exists, an exception is expected to be thrown. SaveMode.Append - when saving a DataFrame to a data source, if data/table already exists, contents of the DataFrame are expected to be appended to existing data. SaveMode.Overwrite - means that when saving a DataFrame to a data source, if data/table already exists, existing data is expected to be overwritten by the contents of the DataFrame. SaveMode.Ignore - means that when saving a DataFrame to a data source, if data already exists, the save operation is expected to not save the contents of the DataFrame and to not change the existing data. This is similar to a CREATE TABLE IF NOT EXISTS in SQL. So if your case, if you want to overwrite your existing data, you should do the following : import org.apache.spark.sql.SaveMode df.write.partitionBy("logdatekey", "hostname").mode(SaveMode.Overwrite).save("C:/temp/results.parquet")
How to overwrite the output directory in spark
I have a spark streaming application which produces a dataset for every minute. I need to save/overwrite the results of the processed data. When I tried to overwrite the dataset org.apache.hadoop.mapred.FileAlreadyExistsException stops the execution. I set the Spark property set("spark.files.overwrite","true") , but there is no luck. How to overwrite or Predelete the files from spark?
UPDATE: Suggest using Dataframes, plus something like ... .write.mode(SaveMode.Overwrite) .... Handy pimp: implicit class PimpedStringRDD(rdd: RDD[String]) { def write(p: String)(implicit ss: SparkSession): Unit = { import ss.implicits._ rdd.toDF().as[String].write.mode(SaveMode.Overwrite).text(p) } } For older versions try yourSparkConf.set("spark.hadoop.validateOutputSpecs", "false") val sc = SparkContext(yourSparkConf) In 1.1.0 you can set conf settings using the spark-submit script with the --conf flag. WARNING (older versions): According to #piggybox there is a bug in Spark where it will only overwrite files it needs to to write it's part- files, any other files will be left unremoved.
since df.save(path, source, mode) is deprecated, (http://spark.apache.org/docs/1.5.0/api/scala/index.html#org.apache.spark.sql.DataFrame) use df.write.format(source).mode("overwrite").save(path) where df.write is DataFrameWriter 'source' can be ("com.databricks.spark.avro" | "parquet" | "json")
From the pyspark.sql.DataFrame.save documentation (currently at 1.3.1), you can specify mode='overwrite' when saving a DataFrame: myDataFrame.save(path='myPath', source='parquet', mode='overwrite') I've verified that this will even remove left over partition files. So if you had say 10 partitions/files originally, but then overwrote the folder with a DataFrame that only had 6 partitions, the resulting folder will have the 6 partitions/files. See the Spark SQL documentation for more information about the mode options.
The documentation for the parameter spark.files.overwrite says this: "Whether to overwrite files added through SparkContext.addFile() when the target file exists and its contents do not match those of the source." So it has no effect on saveAsTextFiles method. You could do this before saving the file: val hadoopConf = new org.apache.hadoop.conf.Configuration() val hdfs = org.apache.hadoop.fs.FileSystem.get(new java.net.URI("hdfs://localhost:9000"), hadoopConf) try { hdfs.delete(new org.apache.hadoop.fs.Path(filepath), true) } catch { case _ : Throwable => { } } Aas explained here: http://apache-spark-user-list.1001560.n3.nabble.com/How-can-I-make-Spark-1-0-saveAsTextFile-to-overwrite-existing-file-td6696.html
df.write.mode('overwrite').parquet("/output/folder/path") works if you want to overwrite a parquet file using python. This is in spark 1.6.2. API may be different in later versions
val jobName = "WordCount"; //overwrite the output directory in spark set("spark.hadoop.validateOutputSpecs", "false") val conf = new SparkConf().setAppName(jobName).set("spark.hadoop.validateOutputSpecs", "false"); val sc = new SparkContext(conf)
This overloaded version of the save function works for me: yourDF.save(outputPath, org.apache.spark.sql.SaveMode.valueOf("Overwrite")) The example above would overwrite an existing folder. The savemode can take these parameters as well (https://spark.apache.org/docs/1.4.0/api/java/org/apache/spark/sql/SaveMode.html): Append: Append mode means that when saving a DataFrame to a data source, if data/table already exists, contents of the DataFrame are expected to be appended to existing data. ErrorIfExists: ErrorIfExists mode means that when saving a DataFrame to a data source, if data already exists, an exception is expected to be thrown. Ignore: Ignore mode means that when saving a DataFrame to a data source, if data already exists, the save operation is expected to not save the contents of the DataFrame and to not change the existing data.
Spark – Overwrite the output directory: Spark by default doesn’t overwrite the output directory on S3, HDFS, and any other file systems, when you try to write the DataFrame contents to an existing directory, Spark returns runtime error hence. To overcome this Spark provides an enumeration org.apache.spark.sql.SaveMode.Overwrite to overwrite the existing folder. We need to use this Overwrite as an argument to mode() function of the DataFrameWrite class, for example. df. write.mode(SaveMode.Overwrite).csv("/tmp/out/foldername") or you can use the overwrite string. df.write.mode("overwrite").csv("/tmp/out/foldername") Besides Overwrite, SaveMode also offers other modes like SaveMode.Append, SaveMode.ErrorIfExists and SaveMode.Ignore For older versions of Spark, you can use the following to overwrite the output directory with the RDD contents. sparkConf.set("spark.hadoop.validateOutputSpecs", "false") val sparkContext = SparkContext(sparkConf)
If you are willing to use your own custom output format, you would be able to get the desired behaviour with RDD as well. Have a look at the following classes: FileOutputFormat, FileOutputCommitter In file output format you have a method named checkOutputSpecs, which is checking whether the output directory exists. In FileOutputCommitter you have the commitJob which is usually transferring data from the temporary directory to its final place. I wasn't able to verify it yet (would do it, as soon as I have few free minutes) but theoretically: If I extend FileOutputFormat and override checkOutputSpecs to a method that doesn't throw exception on directory already exists, and adjust the commitJob method of my custom output committer to perform which ever logic that I want (e.g. Override some of the files, append others) than I may be able to achieve the desired behaviour with RDDs as well. The output format is passed to: saveAsNewAPIHadoopFile (which is the method saveAsTextFile called as well to actually save the files). And the Output committer is configured at the application level.