【Hadoop综合实践】手机卖场大数据综合项目分析

news2025/1/16 14:10:53

🚀 本文章实现了基于MapReduce的手机浏览日志分析
🚀 文章简介:主要包含了数据生成部分,数据处理部分,数据存储部分与数据可视化部分
🚀 【本文仅供参考】其中需求实现的方式有多种,提供的代码并非唯一写法,选择适合的方式即可。

目录

        • 手机日志分析需求
        • 数据生成工具类
        • 模拟数据生成类
        • MapReduce程序需求编写

手机日志分析需求

  • 本文主要实现以下需求
  1. 编写数据生成器生成1G~10G大小的数据,字段必须包括id,日期,手机号码、型号、操作系统字段。
  2. 需要将手机号码4~9为掩码处理。
  3. 分析2021年、2022年操作系统市场占比、手机型号市场占比情况
  4. 分析2022年手机运营商市场占比情况
  5. 分析数据存储到HDFS集群/ana/phone节点下面
  6. 将分析结果存储到Mysql,并进行数据可视化

数据生成工具类

  • 手机号码随机生成
  • 可以采用随机数生成的方式,结合三大运营商的号码前三位数为规则进行生成 代码如下
/**
 * @Description  生成三大运营商的手机号
 * @Author 湧哥
 * @Version 1.0
 */
/**
 * 中国移动手机号段:
 * 134、135、136、137、138、139、147、150、151、152、157、158、159、172、178、182、183、184、187、188、198、1703、1705、1706
 * 中国联通手机号段:
 * 130、131、132、145、155、156、166、171、175、176、185、186、1704、1707、1708、1709
 * 中国电信手机号段:
 * 133、153、173、177、180、181、189、191、193、199、1700、1701、1702
 * 腾讯云API https://market.cloud.tencent.com/products/31101
 */
public class PhoneNumberGenerator {
    //生成一万个手机号码,只需将 generatePhoneNumbers 方法中的参数 count 修改为 10000 即可
    //移动
    private static final String[] CHINA_MOBILE_PREFIX = {"134", "139", "150", "151", "182"};
    //联通
    private static final String[] CHINA_UNICOM_PREFIX = {"130","155","186"};
    //电信
    private static final String[] CHINA_TELECOM_PREFIX = {"133","153","180","181","189"};

    public static void main(String[] args) {
        String phoneNumbers = generatePhoneNumbers(1);
            System.out.println(phoneNumbers);
    }

    public static String generatePhoneNumbers(int count) {
        String phoneNumber=null;
        Random random = new Random();
        for (int i = 0; i < count; i++) {
            String prefix;
            int operatorIndex = random.nextInt(3);
            switch (operatorIndex) {
                case 0:
                    prefix = CHINA_MOBILE_PREFIX[random.nextInt(CHINA_MOBILE_PREFIX.length)];
                    break;
                case 1:
                    prefix = CHINA_UNICOM_PREFIX[random.nextInt(CHINA_UNICOM_PREFIX.length)];
                    break;
                default:
                    prefix = CHINA_TELECOM_PREFIX[random.nextInt(CHINA_TELECOM_PREFIX.length)];
            }
            phoneNumber = prefix + generateRandomNumber(random, 11 - prefix.length());
        }
        return replaceCharacters(phoneNumber,3,8);
    }
    private static String replaceCharacters(String input, int startIndex, int endIndex) {
        if (input == null || input.length() < endIndex) {
            return input;
        }

        StringBuilder sb = new StringBuilder(input);

        for (int i = startIndex; i <= endIndex; i++) {
            sb.setCharAt(i, '*');
        }

        return sb.toString();
    }
    private static String generateRandomNumber(Random random, int length) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            sb.append(random.nextInt(10));
        }
        return sb.toString();
    }
}
  • 运营商解析的其中一种方式 【采用接口分析】
  • 这里可以使用鹅厂或者其他厂商开发的接口进行运营商识别 申请获取对应的秘钥即可 例子如下
/**
 * @Description
 * @Author 湧哥
 * @Version 1.0
 */

public class PhoneOperator {
    public static String calcAuthorization(String source, String secretId, String secretKey, String datetime)
            throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
        String signStr = "x-date: " + datetime + "\n" + "x-source: " + source;
        Mac mac = Mac.getInstance("HmacSHA1");
        Key sKey = new SecretKeySpec(secretKey.getBytes("UTF-8"), mac.getAlgorithm());
        mac.init(sKey);
        byte[] hash = mac.doFinal(signStr.getBytes("UTF-8"));
        String sig = new BASE64Encoder().encode(hash);

        String auth = "hmac id=\"" + secretId + "\", algorithm=\"hmac-sha1\", headers=\"x-date x-source\", signature=\"" + sig + "\"";
        return auth;
    }

    public static String urlencode(Map<?, ?> map) throws UnsupportedEncodingException {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<?, ?> entry : map.entrySet()) {
            if (sb.length() > 0) {
                sb.append("&");
            }
            sb.append(String.format("%s=%s",
                    URLEncoder.encode(entry.getKey().toString(), "UTF-8"),
                    URLEncoder.encode(entry.getValue().toString(), "UTF-8")
            ));
        }
        return sb.toString();
    }

    public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
        //云市场分配的密钥Id
        String secretId = "xx";
        //云市场分配的密钥Key
        String secretKey = "xx;
        String source = "market";

        Calendar cd = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        String datetime = sdf.format(cd.getTime());
        // 签名
        String auth = calcAuthorization(source, secretId, secretKey, datetime);
        // 请求方法
        String method = "POST";
        // 请求头
        Map<String, String> headers = new HashMap<String, String>();
        headers.put("X-Source", source);
        headers.put("X-Date", datetime);
        headers.put("Authorization", auth);

        // 查询参数
        Map<String, String> queryParams = new HashMap<String, String>();
        queryParams.put("mobile","XXX");
        // body参数
        Map<String, String> bodyParams = new HashMap<String, String>();
        // url参数拼接
        String url = "https://service-8c43o60c-1253285064.gz.apigw.tencentcs.com/release/sms";
        if (!queryParams.isEmpty()) {
            url += "?" + urlencode(queryParams);
        }

        BufferedReader in = null;
        try {
            URL realUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);
            conn.setRequestMethod(method);

            // request headers
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                conn.setRequestProperty(entry.getKey(), entry.getValue());
            }

            // request body
            Map<String, Boolean> methods = new HashMap<>();
            methods.put("POST", true);
            methods.put("PUT", true);
            methods.put("PATCH", true);
            Boolean hasBody = methods.get(method);
            if (hasBody != null) {
                conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

                conn.setDoOutput(true);
                DataOutputStream out = new DataOutputStream(conn.getOutputStream());
                out.writeBytes(urlencode(bodyParams));
                out.flush();
                out.close();
            }

            // 定义 BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            String result = "";
            while ((line = in.readLine()) != null) {
                result += line;
            }
            System.out.println(result);
        } catch (Exception e) {
            System.out.println(e);
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }
}

结果如下 (另一种方式为:直接根据前三位手机号进行判断)
在这里插入图片描述

模拟数据生成类

  • 数据生成器 id,日期,手机号码、型号、操作系统

/**
 * @Description
 *  数据生成器  id,日期,手机号码、型号、操作系统
 *  id:UUID 随机生成  日期:2021 2022 手机号码:三大运营商  型号:Apple HuaWei Oppo Vivo Meizu Nokia  操作系统:Apple ios Harmony Samsung
 *  1.分析2021年、2022年操作系统市场占比、手机型号市场占比情况
 *  2.分析2022年手机运营商市场占比情况
 *  3.分析数据存储到HDFS集群/ana/phone节点下面
 *  4.将分析结果存储到Mysql,并进行数据可视化
 * @Author 湧哥
 * @Version 1.0
 */
public class DataGenerator {

    public static void main(String[] args) {
        try {
            BufferedWriter writer = new BufferedWriter(new FileWriter("data/phone.log"));
            for (int i = 0; i < 1000; i++) {
                //UUID随机生成   id,日期,手机号码、型号、操作系统
                String id = UUID.randomUUID().toString();
                String date = getRandomDate();
                String phoneNumber = PhoneNumberGenerator.generatePhoneNumbers(1);
                String model = getRandomModel();
                String operatingSystem = getRandomOperatingSystem();
                String line = id + "," + date + "," + phoneNumber + "," + model + "," + operatingSystem;
                writer.write(line);
                writer.newLine();
            }
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static String getRandomDate() {
        Random random = new Random();
        int year = random.nextInt(2) == 0 ? 2021 : 2022;
        int month = random.nextInt(12) + 1;
        int dayOfMonth;

        if (month == 2) {
            dayOfMonth = random.nextInt(28) + 1;
        } else if (month == 4 || month == 6 || month == 9 || month == 11) {
            dayOfMonth= random.nextInt(30) + 1;
        } else {
            dayOfMonth= random.nextInt(31) + 1;
        }

        return year + "-" +
                (month < 10 ? "0" : "") +
                month+ "-" +
                (dayOfMonth<10? "0":"")+
                dayOfMonth ;
    }

    private static String getRandomPhoneNumber() {
        Random random = new Random();
        StringBuilder phoneNumber = new StringBuilder("1");

        for (int i = 0; i < 10; i++) {
            phoneNumber.append(random.nextInt(10));
        }

        return phoneNumber.toString();
    }

    private static String getRandomModel() {
        String[] models = {"Apple", "HuaWei", "Oppo", "Vivo", "Meizu", "Nokia"};
        return models[new Random().nextInt(models.length)];
    }

    private static String getRandomOperatingSystem() {
        String[] operatingSystems = {"Apple", "HarmonyOS", "Samsung","iOS"};
        return operatingSystems[new Random().nextInt(operatingSystems.length)];
    }
}

结果如下
在这里插入图片描述

MapReduce程序需求编写

  • 分析2021年、2022年操作系统市场占比、手机型号市场占比情况

/**
 * @Description
 * @Author 湧哥
 * @Version 1.0
 */
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Partitioner;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class PhoneOSAnalysis {
    private static int totalCount;
    private static int lineCount2021 = 0;
    private static int lineCount2022 = 0;
    public static class TokenizerMapper extends Mapper<Object, Text, Text, DoubleWritable> {
        private final static DoubleWritable one = new DoubleWritable(1);
        private Text word = new Text();

        public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
            String[] fields = value.toString().split(",");
            if (fields.length >= 5) {
                // 操作系统市场占比
                word.set(fields[1].substring(0, 4) + "-OS-" + fields[4]);
                context.write(word, one);
                // 手机型号市场占比
                word.set(fields[1].substring(0, 4) + "-Model-" + fields[3]);
                context.write(word, one);
            }
        }
    }

    public static class MarketShareReducer extends Reducer<Text, DoubleWritable, Text, DoubleWritable> {
        private DoubleWritable result = new DoubleWritable();

        public void reduce(Text key, Iterable<DoubleWritable> values, Context context)
                throws IOException, InterruptedException {
            double sum = 0;
            for (DoubleWritable val : values) {
                //这里会根据分组的key来计算sum
                sum += val.get();
            }
            int yearTotalCount = key.toString().contains("2021") ? lineCount2021 : lineCount2022;
            double percentage = sum / yearTotalCount;
            result.set(percentage);
            context.write(key, result);
        }
    }

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(conf);
        Path inputPath = new Path("data/phone.log");
        FSDataInputStream inputStream = fs.open(inputPath);
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(fs.open(inputPath)))) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.contains("2021")) {
                    lineCount2021++;
                } else if (line.contains("2022")) {
                    lineCount2022++;
                }
            }
        }

//        totalCount = Math.max(lineCount2021, lineCount2022);

        Job job = Job.getInstance(conf, "market share analysis");
        job.setJarByClass(PhoneOSAnalysis.class);
        job.setMapperClass(TokenizerMapper.class);

        // 设置自定义分区器
        job.setPartitionerClass(CustomPartitioner.class);
        job.setNumReduceTasks(2);

        job.setReducerClass(MarketShareReducer.class);

        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(DoubleWritable.class);
        Path outputPath = new Path("data/result");
        if (fs.exists(outputPath)) {
            fs.delete(outputPath, true);
        }
        FileInputFormat.addInputPath(job, new Path("data/phone.log"));
        FileOutputFormat.setOutputPath(job, new Path(String.valueOf(outputPath)));
        //        TextInputFormat.addInputPath(job, new Path("hdfs://192.168.192.100:8020/"));
//        TextInputFormat.outInputPath(job, new Path("hdfs://192.168.192.100:8020/"));
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }

    public static class CustomPartitioner extends Partitioner<Text, DoubleWritable> {

        @Override
        public int getPartition(Text key, DoubleWritable value, int numPartitions) {
            // 根据年份进行分区
            if (key.toString().contains("2021")) {
                return 0;
            } else {
                return 1;
            }
        }
    }
}
  • 分析2022年手机运营商市场占比情况

/**
 * @Description
 * @Author 湧哥
 * @Version 1.0
 */
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class OperatorMR {
    private static int lineCount2022 = 0;

    public static class TokenizerMapper extends Mapper<Object, Text, Text, DoubleWritable> {
        private final static DoubleWritable one = new DoubleWritable(1);
        private Text word = new Text();

        public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
            String[] fields = value.toString().split(",");
            if (fields.length >= 3 && fields[1].contains("2022")) {
                // 手机运营商市场占比
                word.set(fields[1].substring(0, 4) + "-Operator-" + getCarrier(fields[2]));
                context.write(word, one);
            }
        }

        private String getCarrier(String phoneNumber) {
            String prefix = phoneNumber.substring(0, 3);
            switch (prefix) {
                //"133","153","180","181","189"
                case "133":
                case "153":
                case "180":
                case "181":
                case "189":
                    return "电信";
                //"130","155","186"
                case "130":
                case "155":
                case "186":
                    return "联通";
                default:
                    return "移动";
            }
        }
    }

    public static class MarketShareReducer extends Reducer<Text, DoubleWritable, Text, DoubleWritable> {
        private DoubleWritable result = new DoubleWritable();

        public void reduce(Text key, Iterable<DoubleWritable> values, Context context)
                throws IOException, InterruptedException {
            double sum = 0;
            for (DoubleWritable val : values) {
                sum += val.get();
            }
            double percentage = sum / lineCount2022;
            result.set(percentage);
            context.write(key, result);
        }
    }

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(conf);
        Path inputPath = new Path("data/phone.log");

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(fs.open(inputPath)))) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.contains("2022")) {
                    lineCount2022++;
                }
            }
        }

        Job job = Job.getInstance(conf, "PhoneOperator");
        job.setJarByClass(OperatorMR.class);
        job.setMapperClass(TokenizerMapper.class);
        job.setReducerClass(MarketShareReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(DoubleWritable.class);

        Path outputPath = new Path("data/result-phone");

        if (fs.exists(outputPath)) {
            fs.delete(outputPath, true);
        }
//        TextInputFormat.addInputPath(job, new Path("hdfs://192.168.192.100:8020/"));
//        TextInputFormat.outInputPath(job, new Path("hdfs://192.168.192.100:8020/"));
        FileInputFormat.addInputPath(job, new Path("data/phone.log"));
        FileOutputFormat.setOutputPath(job, outputPath);
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

结果如下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

-将分析结果存储到Mysql,并进行数据可视化

package com.yopai.mrmysql;

/**
 * @Description
 * @Author 湧哥
 * @Version 1.0
 */

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.db.DBConfiguration;
import org.apache.hadoop.mapreduce.lib.db.DBOutputFormat;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;


public class OPMysqlMR {
    private static int lineCount2022 = 0;

    public static class TokenizerMapper extends Mapper<Object, Text, Text, DoubleWritable> {
        private final static DoubleWritable one = new DoubleWritable(1);
        private Text word = new Text();

        public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
            String[] fields = value.toString().split(",");
            if (fields.length >= 3 && fields[1].contains("2022")) {
                // 手机运营商市场占比
                word.set(fields[1].substring(0, 4) + "-Operator-" + getCarrier(fields[2]));
                context.write(word, one);
            }
        }

        private String getCarrier(String phoneNumber) {
            String prefix = phoneNumber.substring(0, 3);
            switch (prefix) {
                case "133":
                case "153":
                case "180":
                case "181":
                case "189":
                    return "电信";
                case "130":
                case "155":
                case "186":
                    return "联通";
                default:
                    return "移动";
            }
        }
    }

    public static class MarketShareReducer extends Reducer<Text, DoubleWritable, DBOutputWritable, NullWritable> {
        private DoubleWritable result = new DoubleWritable();

        public void reduce(Text key, Iterable<DoubleWritable> values, Context context)
                throws IOException, InterruptedException {
            double sum = 0;
            for (DoubleWritable val : values) {
                sum += val.get();
            }
            double percentage = sum / lineCount2022;
            result.set(percentage);
            context.write(new DBOutputWritable(key.toString(), result.get()), NullWritable.get());
        }
    }

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();

        // 设置数据库连接信息
        String dbUrl = "jdbc:mysql://localhost:3306/blog";
        String dbUsername = "root";
        String dbPassword = "Admin2022!";

        DBConfiguration.configureDB(conf, "com.mysql.jdbc.Driver", dbUrl, dbUsername, dbPassword);

        try (Connection connection = DriverManager.getConnection(dbUrl, dbUsername, dbPassword)) {
            String createTableSql = "CREATE TABLE IF NOT EXISTS operator_market_share(operator VARCHAR(255), market_share DOUBLE)";
            PreparedStatement preparedStatement = connection.prepareStatement(createTableSql);
            preparedStatement.executeUpdate();
        }

        FileSystem fs = FileSystem.get(conf);
        Path inputPath = new Path("data/phone.log");

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(fs.open(inputPath)))) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.contains("2022")) {
                    lineCount2022++;
                }
            }
        }

        Job job = Job.getInstance(conf, "PhoneOperator");
        job.setJarByClass(OPMysqlMR.class);

        job.setMapperClass(TokenizerMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(DoubleWritable.class);

        job.setReducerClass(MarketShareReducer.class);
        job.setOutputKeyClass(DBOutputWritable.class);
        job.setOutputValueClass(NullWritable.class);

        // 设置数据库输出
        DBOutputFormat.setOutput(job, "operator_market_share", "operator", "market_share");

        FileInputFormat.addInputPath(job, new Path("data/phone.log"));
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}
package com.yopai.mrmysql;

import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.mapred.lib.db.DBWritable;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Description
 * @Author 湧哥
 * @Version 1.0
 */
public class DBOutputWritable implements Writable, DBWritable {
    private String operator;
    private double market_share;

    public DBOutputWritable() {
    }

    public DBOutputWritable(String operator, double market_share) {
        this.operator = operator;
        this.market_share = market_share;
    }

    @Override
    public void readFields(DataInput in) throws IOException {
        operator = in.readUTF();
        market_share = in.readDouble();
    }

    @Override
    public void write(DataOutput out) throws IOException, IOException {
        out.writeUTF(operator);
        out.writeDouble(market_share);
    }

    @Override
    public void readFields(ResultSet resultSet) throws SQLException {
        // 不需要实现此方法,因为我们只会写入数据到数据库
    }

    @Override
    public void write(PreparedStatement preparedStatement) throws SQLException {
        preparedStatement.setString(1, operator);
        preparedStatement.setDouble(2, market_share);
    }
}

运行结果如下
在这里插入图片描述

  • 可视化操作
package com.yopai.draw;

/**
 * @Description
 * @Author 湧哥
 * @Version 1.0
 */
import java.awt.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PiePlot;
import org.jfree.data.general.DefaultPieDataset;

public class PieChartExample extends JFrame {

    public PieChartExample() {
        // 从数据库获取数据
        DefaultPieDataset dataset = new DefaultPieDataset();
        try {
            String dbUrl = "jdbc:mysql://localhost:3306/blog";
            String dbUsername = "root";
            String dbPassword = "Admin2022!";
            Connection connection = DriverManager.getConnection(dbUrl, dbUsername, dbPassword);
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery("SELECT operator, market_share FROM operator_market_share");
            while (resultSet.next()) {
                String operator = resultSet.getString("operator");
                double marketShare = resultSet.getDouble("market_share");
                dataset.setValue(operator, marketShare);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 创建饼图
        JFreeChart pieChart = ChartFactory.createPieChart(
                "运营商市场占比",   // 图表标题
                dataset,          // 数据集
                true,             // 是否显示图例
                true,             // 是否生成工具提示
                false             // 是否生成URL链接
        );
        // 设置字体以显示中文
        Font font = new Font("宋体", Font.PLAIN, 12);
        pieChart.getTitle().setFont(font);
        pieChart.getLegend().setItemFont(font);
        PiePlot plot = (PiePlot) pieChart.getPlot();
        plot.setLabelFont(font);
        // 添加饼图到面板并显示
        ChartPanel chartPanel = new ChartPanel(pieChart);
        setContentPane(chartPanel);
    }

    public static void main(String[] args) {
        PieChartExample pieChartExample = new PieChartExample();
        pieChartExample.setSize(600, 600);
        pieChartExample.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pieChartExample.setVisible(true);
    }
}

结果如下
在这里插入图片描述
本篇文章到这里结束 需要注意的是每个人的环境不用调用的API会有所差异。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/620021.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从0到1搞定在线OJ

目录 一、在线OJ的的原理 二、在线OJ的使用规则 三、注意事项 1.关于作弊 2.如何防止作弊 3.输入输出格式 4.换行问题 四、经典在线OJ坑人题目以及博主被坑经历 五、提交不成功及解决方法 六、如何得心应手的拿下OJ系统 七、在线OJ的骗分技巧 在线OJ&#xff08;Onl…

管理项目-查询数据

人事管理项目-查询数据模块 后端实现配置文件实体类Dao层测试前端实现1&#xff0e;创建Dept页面2&#xff0e;修改路由3 测试 后端实现 配置文件 在application.yml文件中配置数据库连接、JPA及端口等信息&#xff0c;代码如下&#xff1a; 实体类 配置完成后建立和表结构…

“出海热”仍在持续,进军东南亚市场谁能率先突围?

在油改电的趋势下&#xff0c;伴随钠电池车型的推出&#xff0c;电动两轮车市场被进一步激活。据艾瑞咨询不完全统计与估算&#xff0c;2022年国内两轮电动车销量约5010万辆&#xff0c;较去年增长15.2%&#xff0c;预计2023年销量达到5400万辆。持续增长的销量足以说明当下的国…

JeecgBoot低代码平台 3.5.2,仪表盘版本发布!重磅新功能—支持在线拖拽设计大屏和门户

项目介绍 JeecgBoot是一款企业级的低代码平台&#xff01;前后端分离架构 SpringBoot2.x&#xff0c;SpringCloud&#xff0c;Ant Design&Vue3&#xff0c;Mybatis-plus&#xff0c;Shiro&#xff0c;JWT 支持微服务。强大的代码生成器让前后端代码一键生成! JeecgBoot引领…

北京君正案例:数传网关的集大成者—积木式边缘网关

数传网关的集大成者 USR-M300产品集成了数据的边缘采集、计算、主动上报和数据读写&#xff0c;联动控制&#xff0c;IO采集和控制等功能&#xff0c;采集协议包含标准Modbus协议和多种常见的PLC协议&#xff0c;以及行业专用协议&#xff1b;主动上报采用分组上报方式&#xf…

如何靠自学成为一名网络安全工程师?

1. 前言 说实话&#xff0c;一直到现在&#xff0c;我都认为绝大多数看我这篇文章的读者最后终究会放弃&#xff0c;原因很简单&#xff0c;自学终究是一种适合于极少数人的学习方法&#xff0c;而且非常非常慢&#xff0c;在这个过程中的变数过大&#xff0c;稍有不慎&#…

电脑提示d3dcompiler_47.dll缺失怎么修复?

d3dcompiler_47.dll是 Microsoft 的 DirectX 11 核心组件之一&#xff0c;它主要用于编译和运行 Direct3D 11 应用程序和游戏。如果您的系统中缺少这个 DLL 文件&#xff0c;可能会导致一些程序无法正常运行&#xff0c;很多游戏跟图形处理软件都会运用到。如果电脑提示“找不到…

软考A计划-系统架构师-官方考试指定教程-(1/15)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

Elasticsearch数据库索引及数据操作

目录结构 前言数据库初始化索引操作创建索引获取索引获取所有索引删除索引 数据操作新增POST方式PUT方式 查询主键查询全量查询search 修改全量覆盖部分修改 删除 前言 Elasticsearch安装成功情况下&#xff1b;使用Postman请求操作数据库&#xff1b;浏览器插件实现Elasticsea…

Ubuntu做深度学习+ros怎么分区

正好要重装系统了&#xff0c;学习以下怎么分区 买了铠侠rc20 &#xff0c; 1T用来做Ubuntu系统盘 整理一下要安装的东西&#xff1a; 1.要装cuda &#xff0c;6G&#xff08; 安装在 /usr/local/cuda-11.1 &#xff09; 挂载点 /usr: 存放用户程序&#xff0c;一般在/usr/…

轨道列车自动驾驶和汽车自动驾驶的区别

轨道列车自动驾驶和汽车自动驾驶在一些方面存在明显的区别&#xff0c;主要是由于它们在运行环境和技术要求上的不同。以下是一些主要区别&#xff1a; 运行环境&#xff1a;轨道列车通常在封闭的轨道系统上运行&#xff0c;与其他车辆和行人的交互相对较少。相比之下&#xf…

华为企业组网实例:VRRP+MSTP典型组网配置

华为企业组网实例&#xff1a;VRRPMSTP典型组网配置 VRRPMSTP典型组网配置实验拓扑一、VLAN配置SW3配置SW4配置 二、Trunk配置SW3配置SW4配置SW1配置SW2配置 三、链路聚合四、MSTP配置公共配置SW1配置SW2配置分别在SW3、SW4上面验证 五、VRRP配置sw1配置sw2配置 六、VRRPMSTP的…

Kafka分区消息积压排查指南

针对某个TOPIC只有几个分区积压的场景&#xff0c;可以采用以下方法进行排查&#xff1a; 消息生产是否指定key&#xff1f; 如果指定了消息key&#xff0c;那么消息会指定生产到hash(key)的分区中。如果指定了key&#xff0c;那么有下列几种可能&#xff1a; 生产该key的消息体…

决策树理论

这个文本讨论了决策树模型中的基尼系数。当数据集的所有数据属于同一类时&#xff0c;基尼系数为0&#xff0c;因为此时无需进行分类&#xff0c;已经属于同一类别。因此&#xff0c;选项B是正确的。 决策树是一种用于分类和预测的机器学习模型。基尼系数是衡量数据集纯度的指标…

Laya核心库 + 逻辑 + 资源 移植到 Vite + Vue 3 进行项目开发

Laya项目移植到Vite Vue 3 为什么写这篇文章初遇 Laya结识 Vite Vue 3Vite 构建 Vue 3 项目快速初始化项目VSCode打开项目/目录文件介绍public 目录 安装Volar扩展 导入Laya核心JS库拷贝Laya项目bin/libslibs文件夹放入Vue项目下public文件夹中 导入Laya项目逻辑拷贝Laya项目…

安装db2数据库

1.下载 安装依赖 链接&#xff1a;https://pan.baidu.com/s/1rpZn3H1oN8O0kn8YQMa-gQ?pwd8u9d 提取码&#xff1a;8u9d 需要关闭SELinux 修改配置文件vi /etc/selinux/config&#xff0c;将SELinux置为disabled&#xff0c;并重启机器。 安装缺失的32位包 从RHEL 6开始&…

正式向Win12过渡,微软为Win11开启一大波功能更新

距离 Win 11 正式发布已过去接近两年时间&#xff0c;为了让大家放弃老系统升级 Win 11&#xff0c;微软没少下功夫。 除了各种强制弹窗通知提醒升级外&#xff0c;微软还大刀阔斧砍掉 Win 10 功能更新&#xff0c;并已宣布其最后死期——2025 年 10 月。 然而这一套组合拳下…

『赠书活动 | 第十期』清华社赞助 | 《前端系列丛书》

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 『赠书活动 &#xff5c; 第十期』 本期书籍&#xff1a;《前端系列丛书》 公众号赠书&#xff1a;第二期 参与方式&#xff1a;关注公众号&#xff1a;低调而奢华 …

Dubbo快速入门 —— 基于SpringBoot Starter 开发微服务应用案例 + 知识讲解

&#x1f4a7; D u b b o 快速入门——基于 S p r i n g B o o t S t a r t e r 开发微服务应用案例 知识讲解 \color{#FF1493}{Dubbo快速入门 —— 基于SpringBoot Starter 开发微服务应用案例 知识讲解} Dubbo快速入门——基于SpringBootStarter开发微服务应用案例知识讲解…

7脚手架与jwt

1.引言 企业开发中的前端工程一般不会让程序员自己从零搭建&#xff0c;所以咱们要学会使用别人封装好的架子&#xff0c;或者低代码开发平台&#xff0c;这里 咱们快速上手一个第三方的脚手架。 参考资料 花裤衩 (panjiachen) - Gitee.com 2.安装步骤 # 克隆项目 git clon…