Je suis nouveau sur spark, et je veux utiliser le regroupement et la réduction pour trouver les éléments suivants dans CSV (une ligne par employé):
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
Je voudrais simplifier le sujet CSV avec le groupe par Département, Désignation, État avec des colonnes supplémentaires avec sum (costToCompany) et TotalEmployeeCount
Devrait obtenir un résultat comme:
Dept, Desg, state, empCount, totalCost
Sales,Lead,AP,2,64000
Sales,Lead,LA,3,96000
Sales,Lead,TN,2,64000
Existe-t-il un moyen d'y parvenir en utilisant des transformations et des actions. Ou devrions-nous opter pour des opérations RDD?
Créez une classe (schéma) pour encapsuler votre structure (ce n'est pas nécessaire pour l'approche B, mais cela faciliterait la lecture de votre code si vous utilisez Java)
public class Record implements Serializable {
String department;
String designation;
long costToCompany;
String state;
// constructor , getters and setters
}
Chargement du fichier 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;
}
});
À ce stade, vous avez 2 approches:
Enregistrer une table (en utilisant la classe de schéma définie)
JavaSchemaRDD table = sqlContext.applySchema(rdd_records, Record.class);
table.registerAsTable("record_table");
table.printSchema();
Recherchez la table avec le groupe de requêtes souhaité
JavaSchemaRDD res = sqlContext.sql("
select department,designation,state,sum(costToCompany),count(*)
from record_table
group by department,designation,state
");
Ici, vous pourrez également effectuer toute autre requête que vous souhaitez, en utilisant une approche SQL
Mappage à l'aide d'une clé composite: 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 à l'aide de la clé composite, en additionnant la colonne costToCompany
et en accumulant le nombre d'enregistrements par clé
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);
}
});
Le fichier CSV peut être analysé avec Spark lecteur CSV intégré . Il renverra DataFrame/DataSet lors de la lecture réussie En plus de DataFrame/DataSet, vous appliquez facilement des opérations de type SQL.
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");
Dataset<Row> df = spark.read()
.option("mode", "DROPMALFORMED")
.schema(schema)
.csv("hdfs://path/input.csv");
plus d'option sur la lecture des données du fichier CSV
1. Manière SQL
Enregistrer une table dans spark sql metastore pour effectuer une opération SQL
df.createOrReplaceTempView("employee");
Exécuter une requête SQL sur une trame de données enregistrée
Dataset<Row> sqlResult = spark.sql( "SELECT department, designation, state, SUM(ctc), COUNT(department)" + " FROM employee GROUP BY department, designation, state"); sqlResult.show(); //for testing
Nous pouvons même exécuter SQL directement sur un fichier CSV sans créer de table avec Spark SQL
2. Chaînage d'objets ou programmation ou façon Java
Effectuez l'importation nécessaire pour les fonctions SQL
import static org.Apache.spark.sql.functions.count; import static org.Apache.spark.sql.functions.sum;
Utilisez
groupBy
etagg
sur le cadre de données/jeu de données pour effectuercount
etsum
sur les donnéesDataset<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"
Ce qui suit n'est peut-être pas tout à fait correct, mais il devrait vous donner une idée de la façon de jongler avec les données. Ce n'est pas joli, devrait être remplacé par des classes de cas, etc., mais comme un exemple rapide de la façon d'utiliser l'api spark, j'espère que cela suffit :)
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")
Ou vous pouvez utiliser 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"""
Pour JSON, si votre fichier texte contient un objet JSON par ligne, vous pouvez utiliser sqlContext.jsonFile(path)
pour laisser Spark SQL le charger en tant que SchemaRDD
(le schéma sera automatiquement déduit.) Ensuite, vous pouvez l'enregistrer en tant que table et l'interroger avec SQL. Vous pouvez également charger manuellement le fichier texte en tant que RDD[String]
contenant un objet JSON par enregistrement et utiliser sqlContext.jsonRDD(rdd)
pour le transformer en SchemaRDD
. jsonRDD
est utile lorsque vous devez prétraiter vos données.