Синтаксический анализ CSV как DataFrame / DataSet с Apache Spark и Java
Я новичок в spark, и я хочу использовать group-by & reduce, чтобы найти следующее из CSV (одна строка по занятому):
Department, Designation, costToCompany, State
Sales, Trainee, 12000, UP
Sales, Lead, 32000, AP
Sales, Lead, 32000, LA
Sales, Lead, 32000, TN
Sales, Lead, 32000, AP
Sales, Lead, 32000, TN
Sales, Lead, 32000, LA
Sales, Lead, 32000, LA
Marketing, Associate, 18000, TN
Marketing, Associate, 18000, TN
HR, Manager, 58000, TN
Я хотел бы упростить о CSV с группой по Отдел, Обозначение, Штат С дополнительными столбцами с sum (costToCompany) и TotalEmployeeCount
должен получить результат, как:
Dept, Desg, state, empCount, totalCost
Sales,Lead,AP,2,64000
Sales,Lead,LA,3,96000
Sales,Lead,TN,2,64000
есть ли способ достичь этого с помощью преобразований и действий. Или должны ли мы пойти на операции RDD?
4 ответов
процедура
-
создайте класс (схему) для инкапсуляции вашей структуры (это не требуется для подхода B, но это облегчит чтение вашего кода, если вы используете Java)
public class Record implements Serializable { String department; String designation; long costToCompany; String state; // constructor , getters and setters }
-
загрузка файла CVS (JSON)
JavaSparkContext sc; JavaRDD<String> data = sc.textFile("path/input.csv"); //JavaSQLContext sqlContext = new JavaSQLContext(sc); // For previous versions SQLContext sqlContext = new SQLContext(sc); // In Spark 1.3 the Java API and Scala API have been unified JavaRDD<Record> rdd_records = sc.textFile(data).map( new Function<String, Record>() { public Record call(String line) throws Exception { // Here you can use JSON // Gson gson = new Gson(); // gson.fromJson(line, Record.class); String[] fields = line.split(","); Record sd = new Record(fields[0], fields[1], fields[2].trim(), fields[3]); return sd; } });
на данный момент у вас есть 2 подхода:
SparkSQL А.
-
зарегистрировать таблицу (используя определенную схему Класс)
JavaSchemaRDD table = sqlContext.applySchema(rdd_records, Record.class); table.registerAsTable("record_table"); table.printSchema();
-
запрос таблицы с желаемым запросом-group-by
JavaSchemaRDD res = sqlContext.sql(" select department,designation,state,sum(costToCompany),count(*) from record_table group by department,designation,state ");
здесь вы также сможете сделать любой другой запрос, который вы хотите, используя подход SQL
Б. Искра
-
отображение с помощью составного ключа:
Department
,Designation
,State
JavaPairRDD<String, Tuple2<Long, Integer>> records_JPRDD = rdd_records.mapToPair(new PairFunction<Record, String, Tuple2<Long, Integer>>(){ public Tuple2<String, Tuple2<Long, Integer>> call(Record record){ Tuple2<String, Tuple2<Long, Integer>> t2 = new Tuple2<String, Tuple2<Long,Integer>>( record.Department + record.Designation + record.State, new Tuple2<Long, Integer>(record.costToCompany,1) ); return t2; }
});
-
reduceByKey используя составной ключ, суммируя
costToCompany
столбец и накопление количества записей по ключуJavaPairRDD<String, Tuple2<Long, Integer>> final_rdd_records = records_JPRDD.reduceByKey(new Function2<Tuple2<Long, Integer>, Tuple2<Long, Integer>, Tuple2<Long, Integer>>() { public Tuple2<Long, Integer> call(Tuple2<Long, Integer> v1, Tuple2<Long, Integer> v2) throws Exception { return new Tuple2<Long, Integer>(v1._1 + v2._1, v1._2+ v2._2); } });
CSV-файл может быть проанализирован с помощью Spark встроенный CSV reader. Он вернется DataFrame / DataSet при успешном чтении файла. На вершине DataFrame / DataSet, вы применяете SQL-подобные операции легко.
Используя Искру 2.x (и выше) С Java
создать объект SparkSession aka spark
import org.apache.spark.sql.SparkSession;
SparkSession spark = SparkSession
.builder()
.appName("Java Spark SQL Example")
.getOrCreate();
создать схему для строки с StructType
import org.apache.spark.sql.types.StructType;
StructType schema = new StructType()
.add("department", "string")
.add("designation", "string")
.add("ctc", "long")
.add("state", "string");
создать фрейм данных из CSV-файла и применить схема к нему
Dataset<Row> df = spark.read()
.option("mode", "DROPMALFORMED")
.schema(schema)
.csv("path/input.csv");
дополнительная опция для чтения данных из CSV-файла
теперь мы можем агрегировать данные двумя способами
1. В SQL способом
зарегистрировать таблицу в spark sql metastore для выполнения операции SQL
df.createOrReplaceTempView("employee");
выполнить SQL-запрос на зарегистрированном фрейме данных
Dataset<Row> sqlResult = spark.sql( "SELECT department, designation, state, SUM(ctc), COUNT(department)" + " FROM employee GROUP BY department, designation, state"); sqlResult.show(); //for testing
мы можем даже выполнить SQL непосредственно в CSV-файле без создания таблицы с Spark В SQL
2. Цепочка объектов или программирование или Java-подобный способ
выполните необходимый импорт для функций sql
import static org.apache.spark.sql.functions.count; import static org.apache.spark.sql.functions.sum;
использовать
groupBy
иagg
на dataframe / dataset для выполненияcount
иsum
по даннымDataset<Row> dfResult = df.groupBy("department", "designation", "state") .agg(sum("ctc"), count("department")); // After Spark 1.6 columns mentioned in group by will be added to result by default dfResult.show();//for testing
зависимые библиотеки
"org.apache.spark" % "spark-core_2.11" % "2.0.0"
"org.apache.spark" % "spark-sql_2.11" % "2.0.0"
следующее Может быть не совсем правильным, но это должно дать вам некоторое представление о том, как жонглировать данными. Это не очень красиво, следует заменить классами case и т. д., Но в качестве быстрого примера того, как использовать Spark api, я надеюсь, что этого достаточно :)
val rawlines = sc.textfile("hdfs://.../*.csv")
case class Employee(dep: String, des: String, cost: Double, state: String)
val employees = rawlines
.map(_.split(",") /*or use a proper CSV parser*/
.map( Employee(row(0), row(1), row(2), row(3) )
# the 1 is the amount of employees (which is obviously 1 per line)
val keyVals = employees.map( em => (em.dep, em.des, em.state), (1 , em.cost))
val results = keyVals.reduceByKey{ a,b =>
(a._1 + b._1, b._1, b._2) # (a.count + b.count , a.cost + b.cost )
}
#debug output
results.take(100).foreach(println)
results
.map( keyval => someThingToFormatAsCsvStringOrWhatever )
.saveAsTextFile("hdfs://.../results")
или вы можете использовать SparkSQL:
val sqlContext = new SQLContext(sparkContext)
# case classes can easily be registered as tables
employees.registerAsTable("employees")
val results = sqlContext.sql("""select dep, des, state, sum(cost), count(*)
from employees
group by dep,des,state"""
для JSON, если ваш текстовый файл содержит один объект JSON в строке, вы можете использовать sqlContext.jsonFile(path)
чтобы Spark SQL загрузил его как SchemaRDD
(схема будет выведена автоматически). Затем вы можете зарегистрировать его как таблицу и запросить ее с помощью SQL. Вы также можете вручную загрузить текстовый файл как RDD[String]
содержит один объект JSON на запись и использует sqlContext.jsonRDD(rdd)
чтобы превратить его в SchemaRDD
. jsonRDD
полезно, когда вам нужно предварительно обработать ваши данные.