我们在哪? 我们去哪?

在本系列的前三部分中,您学习了如何:

  • 提取数据
  • 分析数据框中的数据
  • 联接数据

您可能会争辩说,到目前为止所做的一切都可以使用传统的关系数据库来完成。 您将完全正确。 现在是时候通过添加其他两个数据源来发现Spark的某些功能。

更多数据为何?

昨天早上,当您庆祝国际咖啡日时,您听到了销售副总裁与新销售人员的谈话。 他们在讨论销售区,人们在哪里赚钱以及该地区的零售商店。 快速提醒您,您的公司将运动器材批发出售给美国各地的零售店。 尽管您不完全了解他们的销售术语,但是您对他们对数据的了解感到非常沮丧。 您必须跳进去:“我可以分析销售数据,将其与每个邮政编码的中位数收入和该区域的人口规模相交,然后可以查看哪个邮政编码需要更多关注。”

您知道您会通过这样的陈述结识一些新朋友。 现在您需要交付。

甚至更多的数据!

您第一次访问IBM的developerWorks是很好的。 但是,您还需要两个额外的数据集:每个邮政编码的人口和每个邮政编码的收入。

对于这些数据集,取决于两个美国主管部门: 美国人口普查局和美国国税局(IRS) 。 出于本教程的目的,使用了人口普查局的精选版本,因为原始数据难以理解。

尝试使用IRS

使用IRS数据,第一个练习是使用原始IRS数据集查找您销售区域中年收入超过预定义的$ 75,000的家庭数。

您可以从GitHub下载代码和数据 。 对于这一部分,实验位于net.jgp.labs.informix2spark.l4xx程序包中。 数据在数据目录中。 美国国税局(IRS)提供了数据集的技术说明。 它名为14zpdoc.doc ,可在存储库的数据目录中找到。

基本上,每个区域都由其邮政编码定义。 根据收入等级,每个区域分为六个调整后的总收​​入(AGI)组:

  1. 25,000美元以下
  2. 25,000美元至50,000美元以下
  3. 介于$ 50,000至$ 75,000以下
  4. 75,000美元至100,000美元以下
  5. $ 100,000至$ 200,000以下
  6. 超过$ 200,000

本教程的目标是三个高收入人群。

提供所有代码以给您全局视图。

package net.jgp.labs.informix2spark.l400;import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.SparkSession;public class HouseholdsAboveMedianRevenuePerZipApp {public static void main(String[] args) {HouseholdsAboveMedianRevenuePerZipApp app = new HouseholdsAboveMedianRevenuePerZipApp();

通过传递邮政编码来运行应用程序以进行分析。

app.start(27514);}private void start(int zip) {

您在本地模式下创建一个Spark会话。

SparkSession spark = SparkSession.builder().appName("Number of households in your ZIP Code").master("local").getOrCreate();

在Spark中加载CSV文件很简单:将format方法与csv参数一起使用。 CSV加载程序接受许多选项(众所周知,CSV可能很棘手)。 IRS文件遵循一个非常简单的方案,在此方案中使用两个选项:

  • 文件的第一行是标题
  • 让Spark推断模式,以便它可以确定要使用的数据类型。

如果您没有Spark推断架构,则此数据框的所有列都将被视为字符串。 CSV解析还有更多选项,每个选项在我的Blog中都有介绍。

最后,请注意文件名使用通配符。 Spark将所有与14zpallagi*.csv匹配的文件加载到数据目录中。 您可以在此处使用正则表达式:

  • 14zpallagi*.csv读取所有以14zpallagi开头并以.csv结尾的文件,
  • 14zpallagi-part[1-3].csv读取: 14zpallagi-part1.csv14zpallagi-part2.csv14zpallagi-part3.csv
String filename = "data/14zpallagi*.csv";Dataset<Row> df = spark.read().format("csv").option("inferSchema", "true").option("header", "true").load(filename);

现在,您可以检查加载的数据:

df.printSchema();

首先看一下长模式。 很长一段时间,因为IRS慷慨地共享127列(我已经删除了很多列)。

root|-- STATEFIPS: integer (nullable = true)|-- STATE: string (nullable = true)|-- zipcode: integer (nullable = true)|-- agi_stub: integer (nullable = true)|-- N1: double (nullable = true)|-- mars1: double (nullable = true)|-- MARS2: double (nullable = true)|-- MARS4: double (nullable = true)|-- PREP: double (nullable = true)|-- N2: double (nullable = true)|-- NUMDEP: double (nullable = true)|-- TOTAL_VITA: double (nullable = true)|-- VITA: double (nullable = true)|-- TCE: double (nullable = true)|-- A00100: double (nullable = true)|-- N02650: double (nullable = true)
…

看一下数据样本sample()方法最多包含三个参数,即替换-或记录独立性 ,分数和(可选)种子。

df.sample(true, 0.01, 4589).show(2);System.out.println("Dataframe has " + df.count() + " rows and " + df.columns().length+ " columns.");

我将输出限制为两行!

+---------+-----+-------+--------+-------+------+-------+-----+-------+--------+-------+----------+----+---+-----------+-------+-----------+-------+-----------+-------+--------+-------+--------+-------+--------+-------+-------+-------+--------+-------+---------+------+--------+-------+--------+------+------+------+-------+--------+-------+---------+-------+--------+------+------+------+-------+------+-------+------+-------+------+------+------+------+------+-------+-------+---------+-----------+-------+--------+------+------+-------+--------+-------+--------+-------+--------+-------+--------+-------+-----------+-------+---------+-------+-------+------+------+-------+-------+-------+-------+------+------+------+------+------+------+------+------+------+------+-------+--------+------+------+------+------+------+------+-------+---------+------+------+------+------+------+------+------+------+------+------+-------+---------+-------+---------+-------+-------+-------+-------+-------+--------+-------+--------+
|STATEFIPS|STATE|zipcode|agi_stub|     N1| mars1|  MARS2|MARS4|   PREP|      N2| NUMDEP|TOTAL_VITA|VITA|TCE|     A00100| N02650|     A02650| N00200|     A00200| N00300|  A00300| N00600|  A00600| N00650|  A00650| N00700| A00700| N00900|  A00900| N01000|   A01000|N01400|  A01400| N01700|  A01700|  SCHF|N02300|A02300| N02500|  A02500| N26270|   A26270| N02900|  A02900|N03220|A03220|N03300| A03300|N03270| A03270|N03150| A03150|N03210|A03210|N03230|A03230|N03240| A03240| N04470|   A04470|     A00101| N18425|  A18425|N18450|A18450| N18500|  A18500| N18300|  A18300| N19300|  A19300| N19700|  A19700| N04800|     A04800| N05800|   A05800| N09600| A09600|N05780|A05780| N07100| A07100| N07300| A07300|N07180|A07180|N07230|A07230|N07240|A07240|N07220|A07220|N07260|A07260| N09400|  A09400|N85770|A85770|N85775|A85775|N09750|A09750| N10600|   A10600|N59660|A59660|N59720|A59720|N11070|A11070|N10960|A10960|N11560|A11560| N06500|   A06500| N10300|   A10300| N85530| A85530| N85300| A85300| N11901|  A11901| N11902|  A11902|
+---------+-----+-------+--------+-------+------+-------+-----+-------+--------+-------+----------+----+---+-----------+-------+-----------+-------+-----------+-------+--------+-------+--------+-------+--------+-------+-------+-------+--------+-------+---------+------+--------+-------+--------+------+------+------+-------+--------+-------+---------+-------+--------+------+------+------+-------+------+-------+------+-------+------+------+------+------+------+-------+-------+---------+-----------+-------+--------+------+------+-------+--------+-------+--------+-------+--------+-------+--------+-------+-----------+-------+---------+-------+-------+------+------+-------+-------+-------+-------+------+------+------+------+------+------+------+------+------+------+-------+--------+------+------+------+------+------+------+-------+---------+------+------+------+------+------+------+------+------+------+------+-------+---------+-------+---------+-------+-------+-------+-------+-------+--------+-------+--------+
|        1|   AL|      0|       6|51270.0|4160.0|45860.0|910.0|39250.0|148020.0|50820.0|      80.0|80.0|0.0|2.2652783E7|51270.0|2.3073614E7|44660.0|1.1051815E7|40610.0|264670.0|32640.0|791136.0|31530.0|620013.0|29090.0|96411.0|13720.0|855431.0|31500.0|2331256.0|9340.0|444369.0|14820.0|833058.0|2630.0| 520.0|1842.0|11220.0|292923.0|22620.0|4633716.0|22380.0|420831.0|1080.0| 267.0|2770.0|86011.0|8520.0|92498.0|1140.0|10827.0|   0.0|   0.0|   0.0|   0.0|1660.0|61679.0|47840.0|2499208.0|2.1437993E7|45930.0|761404.0|1440.0|4867.0|45630.0|168515.0|47820.0|962132.0|36310.0|456194.0|45490.0|920293.0|51240.0|1.9692443E7|51230.0|5274998.0|15590.0|77387.0|   0.0|   0.0|21020.0|54852.0|15290.0|25542.0|3230.0|1747.0|   0.0|   0.0|   0.0|   0.0|   0.0|   0.0|1410.0| 459.0|14770.0|124954.0|   0.0|   0.0|   0.0|   0.0| 100.0| 219.0|50690.0|5185930.0|   0.0|   0.0|   0.0|   0.0|   0.0|   0.0|   0.0|   0.0|   0.0|   0.0|51170.0|5204318.0|51230.0|5472274.0|19090.0|33930.0|25450.0|89886.0|28340.0|774418.0|15560.0|243494.0|
|        1|   AL|  35004|       5|  590.0|  40.0|  530.0|  0.0|  300.0|  1660.0|  550.0|       0.0| 0.0|0.0|    74554.0|  590.0|    75493.0|  560.0|    64835.0|  260.0|   150.0|  140.0|   236.0|  130.0|   149.0|  350.0|  310.0|  100.0|  1671.0|  100.0|    364.0|  60.0|  1459.0|  150.0|  3991.0|   0.0|   0.0|   0.0|   90.0|  1796.0|   40.0|   1408.0|  240.0|   939.0|  30.0|   8.0|   0.0|    0.0|   0.0|    0.0|   0.0|    0.0| 130.0| 132.0|   0.0|   0.0|   0.0|    0.0|  450.0|   9296.0|    57663.0|  430.0|  2268.0|   0.0|   0.0|  420.0|   353.0|  450.0|  2766.0|  400.0|  2900.0|  420.0|  2321.0|  590.0|    56931.0|  590.0|   9612.0|    0.0|    0.0|   0.0|   0.0|  310.0|  448.0|   40.0|    3.0| 120.0|  66.0|  70.0|  99.0|   0.0|   0.0| 190.0| 249.0|   0.0|   0.0|   60.0|   228.0|   0.0|   0.0|   0.0|   0.0|   0.0|   0.0|  590.0|  10176.0|   0.0|   0.0|   0.0|   0.0|   0.0|   0.0|  60.0|  59.0|   0.0|   0.0|  580.0|   9179.0|  580.0|   9419.0|    0.0|    0.0|    0.0|    0.0|  200.0|   625.0|  380.0|  1553.0|
+---------+-----+-------+--------+-------+------+-------+-----+-------+--------+-------+----------+----+---+-----------+-------+-----------+-------+-----------+-------+--------+-------+--------+-------+--------+-------+-------+-------+--------+-------+---------+------+--------+-------+--------+------+------+------+-------+--------+-------+---------+-------+--------+------+------+------+-------+------+-------+------+-------+------+------+------+------+------+-------+-------+---------+-----------+-------+--------+------+------+-------+--------+-------+--------+-------+--------+-------+--------+-------+-----------+-------+---------+-------+-------+------+------+-------+-------+-------+-------+------+------+------+------+------+------+------+------+------+------+-------+--------+------+------+------+------+------+------+-------+---------+------+------+------+------+------+------+------+------+------+------+-------+---------+-------+---------+-------+-------+-------+-------+-------+--------+-------+--------+
only showing top 2 rows
The dataframe has 166719 rows and 127 columns.

您不必清理数据集,但我发现它更具可读性。 要清理数据集,请过滤邮政编码,然后删除多余的列。

Dataset<Row> df2 = df.filter(df.col("zipcode").equalTo(zip));String[] colsToDrop = { "STATEFIPS", "mars1", "MARS2", "MARS4", "PREP", "N2","NUMDEP", "TOTAL_VITA", "VITA", "TCE", "A00100", "N02650", "N00200", "A00200","N00300", "A00300", "N00600", "A00600", "N00650", "A00650", "N00700", "A00700","N00900", "A00900", "N01000", "A01000", "N01400", "A01400", "N01700", "A01700","SCHF", "N02300", "A02300", "N02500", "A02500", "N26270", "A26270", "N02900","A02900", "N03220", "A03220", "N03300", "A03300", "N03270", "A03270", "N03150","A03150", "N03210", "A03210", "N03230", "A03230", "N03240", "A03240", "N04470","A04470", "A00101", "N18425", "A18425", "N18450", "A18450", "N18500", "A18500","N18300", "A18300", "N19300", "A19300", "N19700", "A19700", "N04800", "A04800","N05800", "A05800", "N09600", "A09600", "N05780", "A05780", "N07100", "A07100","N07300", "A07300", "N07180", "A07180", "N07230", "A07230", "N07240", "A07240","N07220", "A07220", "N07260", "A07260", "N09400", "A09400", "N85770", "A85770","N85775", "A85775", "N09750", "A09750", "N10600", "A10600", "N59660", "A59660","N59720", "A59720", "N11070", "A11070", "N10960", "A10960", "N11560", "A11560","N06500", "A06500", "N10300", "A10300", "N85530", "A85530", "N85300", "A85300","N11901", "A11901", "N11902", "A11902" };for (String colName : colsToDrop) {df2 = df2.drop(colName);}df2.printSchema();df2.show();System.out.println("Dataframe has " + df2.count() + " rows and " + df2.columns().length + " columns.");

现在看一下结果。 您会获得更多的清晰度。

root|-- STATE: string (nullable = true)|-- zipcode: integer (nullable = true)|-- agi_stub: integer (nullable = true)|-- N1: double (nullable = true)|-- A02650: double (nullable = true)+-----+-------+--------+------+--------+
|STATE|zipcode|agi_stub|    N1|  A02650|
+-----+-------+--------+------+--------+
|   NC|  27514|       1|3590.0| 42542.0|
|   NC|  27514|       2|2030.0| 74332.0|
|   NC|  27514|       3|1040.0| 65651.0|
|   NC|  27514|       4| 800.0| 71410.0|
|   NC|  27514|       5|1690.0|249042.0|
|   NC|  27514|       6|1650.0|843353.0|
+-----+-------+--------+------+--------+
Dataframe has 6 rows and 5 columns.

使用这个较小的数据集,您可以看到您感兴趣的是agi_stub大于3的记录。您想要对它们进行计数,并按邮政编码进行分组,并按N1列中的返回数求和。 在Spark代码中,它提供:

Dataset<Row> df3 = df2.filter(df2.col("agi_stub").$greater(3));df3 = df3.groupBy("zipcode").sum("N1").withColumnRenamed("sum(N1)", "households");df3.show();}
}

你会得到:

+-------+----------+
|zipcode|households|
+-------+----------+
|  27514|    4140.0|
+-------+----------+

这些结果是什么意思? 在此邮政编码中,年收入超过75,000美元的人提交了4,140份纳税申报表。 对于家庭而言,这不是1:1,但是它使您对潜力有了一个很好的了解。

有火花吗?

收入数据集将近200MB,您将只使用其中的一小部分。 精选的人口普查数据仅为400KB。 将此数据集放入您主要用于销售和仓库交易的Informix®数据库中是否有意义? 可能不是。 别误会,Informix完全能够处理这些数据集,但这是它的作用吗?

在您沿着数据科学家的道路前进时,您将添加越来越多的数据集,对其进行实验(并可能使用诸如IBM Watson Studio之类的专用工具)。 但是,您可能不希望每个实验的生产数据库中都包含所有这些数据集。

回到业务

在向销售人员显示这些最初的结果后,您作为一个团队发现了一个很好的潜在收入指标。

基本上,这个想法是:

  • 找到最佳的销售区域。
  • 以关键数据为参考:人口( ref_pop ),您在该领域目前的收入( ref_rev )和收入( ref_inc )。
  • 调整每个邮政编码的潜在收入,比较人口( pop ),收入( rev )和平均收入( inc )。

注意: pl。 索引在本文的其余部分中称为“索引”。 不要将此索引与数据库索引混淆。

对于每个区域或邮政编码,请应用:

因此,这很好,但是您如何在Spark中执行此操作?

从业务到发展

您可以在net.jgp.labs.informix2spark.l420包(名为SalesTargetApp的应用程序)中找到此示例。

初始化

您可以遍历代码,首先进行导入,然后进行初始化。

package net.jgp.labs.informix2spark.l420;import static org.apache.spark.sql.functions.col;
import static org.apache.spark.sql.functions.lit;import java.math.BigDecimal;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.SparkSession;
import org.apache.spark.sql.jdbc.JdbcDialect;
import org.apache.spark.sql.jdbc.JdbcDialects;import net.jgp.labs.informix2spark.utils.Config;
import net.jgp.labs.informix2spark.utils.ConfigManager;
import net.jgp.labs.informix2spark.utils.InformixJdbcDialect;
import net.jgp.labs.informix2spark.utils.K;
import scala.collection.Seq;public class SalesTargetApp {SparkSession spark;public SalesTargetApp() {init();}private void init() {this.spark = SparkSession.builder().appName("Sales Target").master("local").getOrCreate();}public static void main(String[] args) {SalesTargetApp app = new SalesTargetApp();app.start();}

与前面的所有示例一样,所有操作均以start()方法start()

private void start() {Dataset<Row> householdDf = getHouseholdDataframe();Dataset<Row> populationDf = getPopulationDataframe();Dataset<Row> indexDf = joinHouseholdPopulation(householdDf, populationDf);Dataset<Row> salesDf = getSalesData();

您正在通过调用方法来构建四个数据框。 让我们深入研究它们。

家庭数据

此方法与本教程中使用IRS数据的第一个实验非常相似。 首先阅读CSV文件。

private Dataset<Row> getHouseholdDataframe() {String filename = "data/14zpallagi*.csv";Dataset<Row> df = spark.read().format("csv").option("inferSchema", "true").option("header", "true").load(filename);

像在SQL中一样,选择您感兴趣的列。 这是删除所有不需要的列的替代方法。

df = df.select(df.col("zipcode"),df.col("agi_stub"),df.col("N1"),df.col("A02650"),

在最后一列中,您需要每个组的所有纳税申报单上的总收入。 请记住,IRS将数据分为六组。

df.col("N1").multiply(df.col("A02650")));

此操作在数据(N1 * A02650)的末尾创建一列,名为(N1 * A02650) 。 那不是描述性的列名,因此您决定将其重命名为incomedf.columns()返回列名称的列表, df.columns()[df.columns().length - 1]提供最后一列的名称。

df = df.withColumnRenamed(df.columns()[df.columns().length - 1], "income");

因为您不希望分析每个邮政编码的每个AGI,所以需要按邮政编码对不同的AGI类别进行分组。 在执行此操作时,您还可以将所有收入添加到名为total_income的列中。

df = df.groupBy("zipcode").sum("income");df = df.withColumnRenamed(df.columns()[df.columns().length - 1], "total_income");return df;}

如果此时显示数据框,则会看到:

+-------+------------+
|zipcode|total_income|
+-------+------------+
|  35071| 4.7763307E8|
|  36525|   6306850.0|
|  36538|    201690.0|
|  85253|9.45181039E9|
|  85321|  1.290294E7|
+-------+------------+
only showing top 5 rows

人口

到目前为止,您获得的所有经验都非常容易加载人口数据框。

private Dataset<Row> getPopulationDataframe() {String filename = "data/2010+Census+Population+By+Zipcode+(ZCTA).csv";Dataset<Row> df = spark.read().format("csv").option("inferSchema", "true").option("header", "true").load(filename);

重命名列还有另一种方法,但是您需要具有列名才能做到这一点(并且您确实具有列名)。

df = df.withColumnRenamed("Zip Code ZCTA", "zipcode");df = df.withColumnRenamed("2010 Census Population", "pop");return df;}

您的数据框如下所示:

+-------+-----+
|zipcode|  pop|
+-------+-----+
|   1001|16769|
|   1002|29049|
|   1003|10372|
|   1005| 5079|
|   1007|14649|
+-------+-----+
only showing top 5 rows

加入家庭收入和人口

正如您在第3部分中了解到的那样,您将以一种外部方式将邮政编码上的两个数据集结合在一起-这意味着您可能会有一些空值。

private Dataset<Row> joinHouseholdPopulation(Dataset<Row> householdDf,Dataset<Row> populationDf) {Dataset<Row> df = householdDf.join(populationDf,householdDf.col("zipcode").equalTo(populationDf.col("zipcode")),"outer").drop(populationDf.col("zipcode"))

现在,创建一个名为income_per_inh的新列。 此列包含总收入除以人口的结果。 这样就可以估算出每个居民的收入。

.withColumn("income_per_inh",householdDf.col("total_income").divide(populationDf.col("pop")));return df;}

withColumn()允许您使用一个或多个列以及函数在数据withColumn()创建新列。 随着您执行越来越多的转换, withColumn()可能成为您最喜欢的方法。 它比尝试查找要重命名的最后一列更容易使用。

现在,这是新数据框的外观:

+-------+------------+-----+------------------+
|zipcode|total_income|  pop|    income_per_inh|
+-------+------------+-----+------------------+
|   1088|   1144910.0|  670|1708.8208955223881|
|   1238|  7.228838E7| 6047|11954.420373739043|
|   1342|   4992920.0| 1492| 3346.461126005362|
|   2122|1.09356174E9|23479| 46576.16338004174|
|   2142| 1.0935586E8| 3141| 34815.61922954473|
+-------+------------+-----+------------------+
only showing top 5 rows

销售数据

对于销售数据,请重用您为本教程系列的第3部分编写的代码。 您只需要将其放入方法中即可。

private Dataset<Row> getSalesData() {
…return salesDf;}

记住,所有代码都可以在GitHub上找到 。

现在,您的销售数据框包含邮政编码和您的收入。

+-------+-------+
|zipcode|revenue|
+-------+-------+
|  94062|1390.00|
|  94040| 562.00|
|  94022| 448.00|
|  19898|1131.00|
|  74006|1614.00|
+-------+-------+
only showing top 5 rows

合并所有数据集,预测销售额

现在,您已经拥有了所有数据集,并准备建立与销售团队定义的索引。

Dataset<Row> salesIndexDf = salesDf.join(indexDf, salesDf.col("zipcode").equalTo(indexDf.col("zipcode")), "left").drop(indexDf.col("zipcode"));

寻找每位居民的收入。

salesIndexDf = salesIndexDf.withColumn("revenue_by_inh", salesIndexDf.col("revenue").divide(salesIndexDf.col("pop")));

现在排序以找出最佳的销售区域。

salesIndexDf = salesIndexDf.orderBy(col("revenue_by_inh").desc());

提取“最佳行”。 最佳行包含所有值,以用作销售团队最佳部门的参考。

Row bestRow = salesIndexDf.first();double bestRevenuePerInhabitant = ((BigDecimal) bestRow.getAs("revenue_by_inh")).doubleValue();int populationOfBestRevenuePerInhabitant = bestRow.getAs("pop");double incomeOfBestRevenuePerInhabitant = bestRow.getAs("income_per_inh");

接下来,在数据框中创建一列。 您知道可以使用withColumn()方法。 但是,如果要添加具有特定值的列怎么办? 您可以通过以下方式完成此操作:将数字列除以它的值(这样就得到1),然后再乘以该值。

salesIndexDf = salesIndexDf.withColumn("best_revenue_per_inh",salesIndexDf.col("pop").divide(salesIndexDf.col("pop")).multiply(bestRevenuePerInhabitant));

或者,您可以使用lit()静态函数:

salesIndexDf = salesIndexDf.withColumn("pop_of_best",lit(populationOfBestRevenuePerInhabitant));salesIndexDf = salesIndexDf.withColumn("income_of_best",lit(incomeOfBestRevenuePerInhabitant));

现在您准备创建三个索引。

salesIndexDf = salesIndexDf.withColumn("idx_revenue",salesIndexDf.col("best_revenue_per_inh").divide(salesIndexDf.col("revenue_by_inh")));salesIndexDf = salesIndexDf.withColumn("idx_pop",salesIndexDf.col("pop").divide(salesIndexDf.col("pop_of_best")));salesIndexDf = salesIndexDf.withColumn("idx_income",salesIndexDf.col("income_per_inh").divide(salesIndexDf.col("income_of_best")));

现在是时候为每个区域创建最终索引了,它是每个索引的乘积。

salesIndexDf = salesIndexDf.withColumn("index",salesIndexDf.col("idx_revenue").multiply(salesIndexDf.col("idx_pop").multiply(salesIndexDf.col("idx_income"))));

并将该索引应用于现有收入。

salesIndexDf = salesIndexDf.withColumn("potential_revenue",salesIndexDf.col("revenue").multiply(salesIndexDf.col("index")));

您可以删除几列以增强输出。 您可能已经放弃了更多,但还需要使报告看起来足够科学。 最后,请确保您按潜在收入的降序排列。

salesIndexDf = salesIndexDf.drop("idx_income").drop("idx_pop").drop("idx_revenue").drop("income_of_best").drop("pop_of_best").drop("best_revenue_per_inh").orderBy(salesIndexDf.col("potential_revenue").desc());

您可以查看结果数据。

salesIndexDf.show();}
+-------+-------+-----+------------------+------------------+------------------+
|zipcode|revenue|  pop|    income_per_inh|             index| potential_revenue|
+-------+-------+-----+------------------+------------------+------------------+
|  94025|  84.00|40526| 840368.1256477323|1610.5247342457083|135284.07767663948|
|  08540|1499.97|47115|469565.43117903004| 68.11481294046366|102170.17596630729|
|  94086|1200.00|45697| 244836.9227739239| 41.76194133319635| 50114.32959983562|
|  94062|1390.00|25876| 738260.2450146854| 34.85768977158333| 48452.18878250083|
|  80219| 232.00|61296|104358.72308144088| 165.6588621914614|38432.856028419046|
|  94022| 448.00|18500|1081220.6994594594| 80.96352858991159|36271.660808280394|
|  94040| 562.00|32996|257082.76791126197|  48.8167513518355| 27435.01425973155|
|  32256| 438.00|38483|142462.90881688017| 47.21447575076384|20679.940378834563|
|  85016| 584.00|33896| 94057.98914326174| 18.13799917867292|10592.591520344986|
|  94063|5592.00|30949| 59561.31635917154|               1.0|            5592.0|
|  94085| 450.00|21247| 95544.70842942533| 9.395047814603645|  4227.77151657164|
|  74006|1614.00|25750| 63162.90718446602|2.5434469314609442| 4105.123347377964|
|  08002| 654.00|22274|59319.770584538026| 4.410905752434176| 2884.732362091951|
|  60406| 824.00|25460| 36702.44422623723|2.8300506455361445| 2331.961731921783|
|  94026|1451.80| null|              null|              null|              null|
|  19898|1131.00| null|              null|              null|              null|
+-------+-------+-----+------------------+------------------+------------------+

结果与验证

您参考的是邮政编码94063的销售,在这里您以$ 5,592的价格售出。 该区域的索引为1。

根据您建立的索引,您可以看到最大的潜力是在邮政编码94025中,这是加利福尼亚州的Menlo Park(有趣的是,这是Informix的发源地)。 数据显示,这里的家庭平均收入是全国最高的。 因此,有意义的是邮政编码94025中具有更高的销售潜力。

潜力最小的地区是伊利诺伊州蓝岛的60406,这是芝加哥南部人口较少的地区。

您可以查看最具潜力的五个邮政编码:加利福尼亚州门洛帕克,新泽西州普林斯顿,加利福尼亚州桑尼维尔,加利福尼亚州雷德伍德城和科罗拉多州丹佛市,您的销售经理可能会得出结论:在这些领域加大销售力度是很有意义的。

你学到了什么

在本教程系列的第4部分中,您学习了:

  • 如何执行高级分析。 即使您的企业内部可能没有所有数据,也可以从管理或开放数据门户下载外部数据集。
  • 对于来自RDBMS或外部文件的数据,Spark的性能同样出色。
  • 使用所有API或语言,都有不同的方法来获得相同的东西。 最好是始终保持一致性和清晰度。 这将有助于您的代码维护。
  • 如何验证结果。 您的发现与人们有更多钱花在运动器材上的加利福尼亚州的直觉相吻合。

走得更远

  • 要在Mac上安装和理解更好的Informix,请阅读“ Mac 10.12上的Informix 12.10,并带有一些Java 8:苹果,咖啡和一个伟大的数据库的故事 ”(自私无耻的插件;我写了这本书)。
  • 从我的GitHub存储库下载本教程中的所有代码。 如何使用JDBC将IBM Informix数据传输到Apache Spark 。 别忘了喜欢叉!
  • 了解更多接口DatabaseMetaData和JDBC DatabaseMetaData 。
  • 获取策划的美国人口普查局数据集 。
  • CSV摄取选项列表。
  • 为什么销售人员应该像数据科学家一样思考

翻译自: https://www.ibm.com/developerworks/opensource/library/ba-offloading-informix-data-spark-4/index.html

利用数据与其他数据源相关推荐

  1. vue一二级联动清空二级数据_【周一实用技巧】二级联动还不够,自动更新才最牛。Excel 2013利用数据验证条件制作一级、二级联动和自动更新下拉列表...

    Excel 2013实用技巧教程系列 第-9.4-节  下拉列表 下拉列表作为提高数据录入效率和防止错误数据的有效方法,在日常工作中应用非常普遍.除了一级.二级联动列表,小编excel小课堂(ID:e ...

  2. Excel中数据透视表数据源更新的三种方式

    Excel中数据透视表数据源更新的三种方式 1.在原有的基础上重新选择数据源 2.变数据源为表格形式,在创建一个透视表,之后如果添加的数据,选中透视表右键刷新即可 3.利用更新过的数据源再重新创建一个 ...

  3. 【Excel_006】利用数据验证创建二级关联下拉菜单

    本篇博客介绍利用数据验证创建二级关联下拉菜单.可达到筛选器/切片器的效果 数据源 步骤 先做大类验证. 选中源数据,Ctrl+G 调出定位窗口,选择[定位条件],选择[常量] 选择[公式]-[根据所选 ...

  4. 如何利用数据赚钱?大数据价值变现的10种商业0模式及利弊分析

    有人说大数据是「石油」是「黄金」,涂子沛说大数据是「土壤」,而马云说大数据是「生产资料」,我觉得他们说得都对,但是也都不对. 因为大数据就是「大数据」. 当大数据应用在不同的领域和不同的场景下,所产生 ...

  5. 利用数据存储技术实现数据安全合理备份

    企业目前的日常工作越来越依靠信息系统的支撑.各类系统中保存着企业大量的业务数据,这些数据成为企业经营决策的客观依据,是企业成长发展的宝贵资源raid数据恢复.一旦数据在传输.存储.交换等过程中丢失,便 ...

  6. 《Excel 职场手册:260招菜鸟变达人》一第 14 招 利用数据验证记录数据录入时间...

    本节书摘来异步社区<Excel 职场手册:260招菜鸟变达人>一书中的第1章,第14节,作者: 聂春霞 , 佛山小老鼠 责编: 王峰松,更多章节内容可以访问云栖社区"异步社区&q ...

  7. 如何利用数据来支撑设计?

    我最早的时候认为设计就是如何去做出各种新奇的图形.质感和界面,追逐潮流和创意.可是后来发现设计最难的是平衡各方面的因素,在条条框框的限 制中找到方案还要推进下去,并被人看到价值.前者很容易满足,而后者 ...

  8. 使用脚本将数据从辅助数据源传送到 InfoPath 域

    概要 在 Microsoft Office InfoPath 2003中,不能直接将表单中的域绑定到辅助数据源中的数据.但是,本文介绍如何以编程方式转换辅助数据源中的数据,然后在运行时将转换后的数据传 ...

  9. Tableau实战系列浏览 Tableau 环境(三) -在“数据”窗格的数据源中导航

    前言 以下是我为大家准备的几个精品专栏,喜欢的小伙伴可自行订阅,你的支持就是我不断更新的动力哟! MATLAB-30天带你从入门到精通 MATLAB深入理解高级教程(附源码) tableau可视化数据 ...

最新文章

  1. 马云自嘲只会用电脑收发邮件,网友:马老师的话,听听就行了
  2. 15个C++项目列表
  3. WebLogic中修改AdminServer及Managed Server的端口
  4. 《大数据》2015年第3期“研究”——大数据流式计算:应用特征和技术挑战
  5. javascript获取系统时间时区_详解Linux操作系统修改时间和修改时区的方法
  6. 终于搞懂了Python模块之间的相互引用问题
  7. 吴恩达 深度学习 编程作业(2-3)- TensorFlow Tutorial
  8. idea debug的时候 启动起来超级慢
  9. text-transform 文本大小写转换、input checkbok 大小设置、letter-spacing 设置字符间距
  10. android 微信小程序原理,Android开发微信小程序页面的图文教程
  11. 企业数据总线(ESB)和注册服务管理(dubbo)的区别
  12. java中的变量是什么
  13. 最新仿720云全景制作源码|krpano仿720云全景网站源码
  14. java如何重命名class_java – 如何重命名XStream列表元素?
  15. 我对大学的憧憬||每个人都有自己的罗马
  16. golang 解析 --- 进程,线程,协程
  17. 记一次简单高效的吸血鬼算法
  18. input type=file 禁止让用户手动输入
  19. 求斐波那契数列c++实现
  20. 聊聊Dubbox(一):为何选择

热门文章

  1. 实测拼多多售假小米电视机 商家:不是原厂
  2. springboot配置拦截器,在拦截器中获取@RequestBody注解参数和post请求参数以及get请求参数
  3. 计算机显示无法打开打印机,添加打印机时电脑弹出错误窗口“Windows无法打开添加打印机”(适用 Windows OS)...
  4. 常见电池快充技术介绍说明
  5. Hdu 4193 Non-negative Partial Sums (数据结构_单调队列)
  6. android默认视频播放器,WebView不打开android默认视频播放器?
  7. django 加 celery 异步任务配置到成功运行
  8. (六)立创EDA之原理图设计示例
  9. WIFI模块中AP模式和STA模式的区别
  10. echarts最常绘制的图形