私はスパークするのが初めてで、group-by&reduceを使用してCSVから次のものを見つけたいと思っています(1行は雇用されています):
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
Department、Designation、Stateとsum(costToCompany)およびTotalEmployeeCountの追加列を使用して、グループのCSVを単純化したい
次のような結果が得られます。
Dept, Desg, state, empCount, totalCost
Sales,Lead,AP,2,64000
Sales,Lead,LA,3,96000
Sales,Lead,TN,2,64000
変換とアクションを使用してこれを達成する方法はありますか。それとも、RDDの運用に行くべきでしょうか?
クラス(スキーマ)を作成して構造をカプセル化します(アプローチ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つのアプローチがあります。
テーブルを登録します(定義されたスキーマクラスを使用)
JavaSchemaRDD table = sqlContext.applySchema(rdd_records, Record.class);
table.registerAsTable("record_table");
table.printSchema();
希望するQuery-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リーダー)で解析できます。読み取り成功時にDataFrame/DataSetを返しますDataFrame/DataSetの上に、SQLのような操作を簡単に適用します。
spark
というSparkSessionオブジェクトを作成します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");
1. SQLの方法
spark sqlメタストアにテーブルを登録して、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
Spark SQL でテーブルを作成せずに、CSVファイルでSQLを直接実行することもできます。
2.オブジェクトチェーンまたはプログラミングまたはJavaのような方法
SQL関数に必要なインポートを行う
import static org.Apache.spark.sql.functions.count; import static org.Apache.spark.sql.functions.sum;
データフレーム/データセットで
groupBy
およびagg
を使用して、データで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"
以下は完全に正しいわけではありませんが、データをジャグリングする方法についてのアイデアを提供するはずです。それはきれいではなく、ケースクラスなどに置き換える必要がありますが、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の場合、テキストファイルに1行に1つのJSONオブジェクトが含まれている場合、sqlContext.jsonFile(path)
を使用してSpark SQLがSchemaRDD
としてロードします(スキーマは次に、テーブルとして登録し、SQLでクエリすることができます。また、レコードごとに1つのJSONオブジェクトを含むRDD[String]
としてテキストファイルを手動でロードし、sqlContext.jsonRDD(rdd)
を使用してSchemaRDD
として有効にします。jsonRDD
は、データを前処理する必要がある場合に便利です。