📘 대용량 데이터 처리 프로젝트

👉 인구이동 데이터로 2021 서울에서 가장 많이 이사간 지역은 어디인지 알아내기

  • 대용량 데이터 500MB를 읽을 수 있다.
  • 읽어온 데이터를 메모리에 올릴 수 있다.
  • 읽어온 데이터로 데이터 분석을 할 수 있다.

1. 파일 읽기

  • 한 글자 단위로 읽어오는 readByChar() 메서드

      // 한 글자씩 읽어오는 메서드
      public void readByChar(String filename) throws IOException {
        
        FileReader fileReader = new FileReader(filename);
        String fileContents = "";
        while(fileContents.length() <= 1_000_000) {
            char c = (char)fileReader.read();
            fileContents += c;
        }
        System.out.println(fileContents);
      	}
    
  • 한 줄 단위로 읽어오는 readByLine() 메서드

      // 한 줄씩 읽어오는 메서드
      public void readByLine(String filename) throws IOException {
        
          FileReader fileReader = new FileReader(filename);
          BufferedReader reader = new BufferedReader(fileReader);
        
          String str = "";
          while ((str = reader.readLine()) != null) {
              System.out.println(str);
        }
      }
    
  • (향상된) 한 줄 단위로 읽어오는 readByLineImproved() 메서드

      // 한 줄씩 읽어오는 메서드 ver.2
      public void readByLineImproved(String filename) throws IOException {
          try (BufferedReader br = Files.newBufferedReader(
                  Paths.get(filename), StandardCharsets.UTF_8)) {
        
              String line;
              while((line = br.readLine()) != null) {
                  System.out.println(line);
              }
          } catch (IOException e) {
              throw new RuntimeException(e);
          }
      }
    
  • 각각의 메서드로 인구 자료 결과 출력

      public static void main(String[] args) throws IOException {
        
      		String address = "C:\\Users\\82104\\Desktop\\교안\\3주차(221007) 2021년 인구 이동 현황\\2021_인구관련연간자료_20221006_35421.csv";    PopulationStatistics ps = new PopulationStatistics();
          ps.readByChar(address);
          ps.readByLine(address);
          ps.readByLineImproved(address);
            
      }
    
  • 전입, 전출 변수를 가지는 PopulationMove 클래스 설계

      public class PopulationMove {
            
      		private int fromSido; // 전출 코드
          private int toSido;   // 전입 코드
        
          public PopulationMove( int fromSido, int toSido) {
              this.fromSido = fromSido;
              this.toSido = toSido;
          }
        
          public int getToSido() {
              return toSido;
          }
        
          public void setToSido(int toSido) {
              this.toSido = toSido;
          }
        
          public int getFromSido() {
              return fromSido;
          }
        
          public void setFromSido(int fromSido) {
              this.fromSido = fromSido;
          }
      }
    

2. Parsing

👉 원하는 기능

  • filename 입력받아서 파일 읽는 기능 → read(String filename)
  • 읽은 파일을 split하는 기능
  1. 전입, 전출 코드 parsing 메서드 parse() - 한 줄

    split()

     // 전입, 전출코드 parsing - 파일 단위
     public PopulationMove parse(String data) {
        
     		String[] strs = data.split(",");
         int fromSido = Integer.parseInt(strs[6]);
         int toSido = Integer.parseInt(strs[0]);
        
         return new PopulationMove(fromSido,toSido);
        
     }
    

    main 함수

     public static void main(String[] args) throws IOException {
        
           String address = "C:\\Users\\82104\\Desktop\\교안\\3주차(221007) 2021년 인구 이동 현황\\2021_인구관련연간자료_20221006_35421.csv";    
           PopulationStatistics ps = new PopulationStatistics();
        
           String data = "50,130,62000,2021,12,20,26,350,52000,1,1,027,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,528528";
           PopulationMove pm = ps.parse(data);
     		  System.out.println(pm.getFromSido());
           System.out.println(pm.getToSido());
     }
    
  2. 전입, 전출 코드 parsing 메서드 readByFile() - 파일 단위

     // 전입, 전출코드 parsing - 파일 단위
     public List<PopulationMove> readByFile(String filename) throws IOException {
        
         FileReader fileReader = new FileReader(filename);
         BufferedReader reader = new BufferedReader(fileReader);
        
         // parse메서드 이용하여 parsing한 데이터를 리스트에 삽입
         List<PopulationMove> pml = new ArrayList<>();
        
         String str = "";
         while ((str = reader.readLine()) != null) {
             PopulationMove parse = parse(str);
             pml.add(parse);
         }
         reader.close();
         return pml;
        
     }
    

    main함수

     public static void main(String[] args) throws IOException {
        
             String address = "C:\\Users\\82104\\Desktop\\교안\\3주차(221007) 2021년 인구 이동 현황\\2021_인구관련연간자료_20221006_35421.csv";
             PopulationStatistics ps = new PopulationStatistics();
        
             List<PopulationMove> pml = ps.readByFile(address);
             System.out.println(pml.size());
        
         }
    
  3. 인터페이스 사용하여 parsing 메서드 구현

        
    

3. 파일로 저장하고 Parsing

👉 필요한 데이터만 parsing해서 파일로 저장하는 이유

500mb를 매번 읽는 것은 부담

왜 부담이냐? 한번 실행 할 때 매번 500mb씩 파싱을 하기 때문

→ 파일을 메모리로 로드하는데 10초가량 걸리기 때문

  1. 파일 생성하는 createAFile() 메서드

     // 파일 생성하기
     public void createAFile(String filename) {
         File file = new File(filename);
         try {
             file.createNewFile();
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
     }
    

    main()

     public static void main(String[] args) throws IOException {
        
         String address = "C:\\Users\\82104\\Desktop\\교안\\3주차(221007) 2021년 인구 이동 현황\\2021_인구관련연간자료_20221006_35421.csv";
         PopulationStatistics ps = new PopulationStatistics()
        
         ps.createAFile("from_to_exercise.txt");
        
     }
    
  2. 파일에 저장하는 write() 메서드

     public void write(List<String> datas, String filename) {
        
         File file = new File(filename);
         try {
             BufferedWriter bw = new BufferedWriter(new FileWriter(file));
             for(String data : datas) {
                 bw.write(data);
             }
             bw.close();
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
     }
    
     // PopulationMove 타입 -> String 타입 변환
         public String dataToString(PopulationMove pm) {
             return pm.getFromSido() +  "," + pm.getToSido() + "\n";
         }
    

    main()

     public static void main(String[] args) throws IOException {
     		/**
     	   * 전입, 전출 코드를 from_to_exercise.txt에 저장하기
     	   */
         String address = "C:\\Users\\82104\\Desktop\\교안\\3주차(221007) 2021년 인구 이동 현황\\2021_인구관련연간자료_20221006_35421.csv";
         PopulationStatistics ps = new PopulationStatistics();
        
         // 파일 단위로 읽어서 parsing한 데이터
         List<PopulationMove> pml = ps.readByFile(address);
        
         ps.createAFile("from_to_exercise.txt");
        
         // parsing한 데이터를 담기 위한 리스트
         ArrayList<String> strings = new ArrayList<>();
        
         for(PopulationMove pm : pml) {
     				System.out.printf("전출 : %s, 전입 : %s\n",pm.getFromSido(),pm.getToSido()); 
             strings.add(ps.dataToString(pm));
         }
        
         ps.write(strings,"./from_to_exercise.txt");
        
     }
    
  3. 시/도 코드와 시/도 명 매핑

4. 파일 데이터로 데이터 분석

👉 Map으로 어디로 많이 갔는지 개수 세기

  1. 전출 → 전입과 횟수 구하는 메서드 getDataToMap()

     public Map<String,Integer> getDataToMap(List<PopulationMove> pml) {
        
         // String (전출,전입), 그 횟수가 Integer (value)에 저장된 map
         Map<String, Integer> map = new HashMap<>();
         for(PopulationMove pm : pml) {
             String key = pm.getFromSido() + "->" + pm.getToSido();
             if(map.get(key) == null) {
                 map.put(key,1);
             } else {
                 map.put(key,map.getOrDefault(key,0)+1);
             }
         }
         return map;
     }
    

    main 함수

     public static void main(String[] args) throws IOException {
        
     		String address1 = "C:\\Users\\82104\\Desktop\\교안\\3주차(221007) 2021년 인구 이동 현황\\2021_인구관련연간자료_20221006_35421.csv";
         PopulationStatistics ps1 = new PopulationStatistics();
        
         // 전입, 전출 코드만 parsing된 데이터
         List<PopulationMove> pmll = ps1.readByFile(address);
        
         // key가 (전입,전출) 이고 그 횟수가 value에 저장된 map
         Map<String,Integer> map = ps1.getDataToMap(pmll);
        
         ps1.createAFile("./sido_cnt_exercise");
         String targetFile = "sido_cnt_exercise";
        
         ArrayList<String> cntResult = new ArrayList<>();
         for(String key : map.keySet()) {
             String s = String.format("[전출->전입] : [%s], 횟수 : %d\n",key,map.get(key));
             cntResult.add(s);
         }
         ps1.write(cntResult, targetFile);
        
     }
    
  2. heatmap을 그리기 위한 시/도 코드 매핑 메서드 SidoCode() 추가

     public Map<String,Integer> SidoCode() {
             Map<String,Integer> map = new HashMap<>();
             map.put("11",0);
             map.put("26",1);
             map.put("27",2);
             map.put("28",3);
             map.put("29",4);
             map.put("30",5);
             map.put("31",6);
             map.put("36",7);
             map.put("41",8);
             map.put("42",9);
             map.put("43",10);
             map.put("44",11);
             map.put("45",12);
             map.put("46",13);
             map.put("47",14);
             map.put("48",15);
             map.put("50",16);
             return map;
         }
    

    main 함수

     public static void main(String[] args) throws IOException {
        
     		/**
     		 * heatmap 그리기 위한 메서드
     		 */
     		String address2 = "C:\\Users\\82104\\Desktop\\교안\\3주차(221007) 2021년 인구 이동 현황\\2021_인구관련연간자료_20221006_35421.csv";
     		PopulationStatistics ps2 = new PopulationStatistics();
        		
     		List<PopulationMove> pmlll = ps.readByFile(address2);
        		
     		Map<String, Integer> heatMap = ps.getDataToMap(pmlll);
        		
     		ps.createAFile("./for_heatmap_exercise.txt");
     		String heatMapFile = "for_heatmap_exercise.txt";
        		
     		List<String> strings1 = new ArrayList<>();
     		Map<String,Integer> sidoForHeatMap = ps.SidoCode();
     		for(String key : heatMap.keySet()) {
     		    String[] fromto = key.split(",");
     		    String format = String.format("[전출, 전입, 횟수] : [%s, %s, %d]\n", sidoForHeatMap.get(fromto[0]),
     		            sidoForHeatMap.get(fromto[1]), heatMap.get(key));
     		    strings1.add(format);
     		}
     		ps.write(strings,heatMapFile);
        
     }
    
  3. heatmap JavaScript로 그리기

     function getPointCategoryName(point, dimension) {
         var series = point.series,
             isY = dimension === 'y',
             axis = series[isY ? 'yAxis' : 'xAxis'];
         return axis.categories[point[isY ? 'y' : 'x']];
     }
        
     Highcharts.chart('container', {
        
         chart: {
             type: 'heatmap',
             marginTop: 40,
             marginBottom: 80,
             plotBorderWidth: 1
         },
        
         title: {
             text: 'Sales per employee per weekday'
         },
        
         xAxis: {
             categories: ['서울특별시','부산광역시','대구광역시','인천광역시','광주광역시','대전광역시',
             '울산광역시','세종특별자치시','경기도','강원도','충청북도','충청남도','전라북도','전라남도',
             '경상북도','경상남도','제주특별자치도']
         },
        
         yAxis: {
             categories: ['서울특별시','부산광역시','대구광역시','인천광역시','광주광역시','대전광역시',
             '울산광역시','세종특별자치시','경기도','강원도','충청북도','충청남도','전라북도','전라남도',
             '경상북도','경상남도','제주특별자치도'],
             title: null,
             reversed: true
         },
        
         accessibility: {
             point: {
                 descriptionFormatter: function (point) {
                     var ix = point.index + 1,
                         xName = getPointCategoryName(point, 'x'),
                         yName = getPointCategoryName(point, 'y'),
                         val = point.value;
                     return ix + '. ' + xName + ' sales ' + yName + ', ' + val + '.';
                 }
             }
         },
        
         colorAxis: {
             min: 0,
             minColor: '#FFFFFF',
             maxColor: Highcharts.getOptions().colors[0]
         },
        
         legend: {
             align: 'right',
             layout: 'vertical',
             margin: 0,
             verticalAlign: 'top',
             y: 25,
             symbolHeight: 280
         },
        
         tooltip: {
             formatter: function () {
                 return '<b>' + getPointCategoryName(this.point, 'x') + '</b> sold <br><b>' +
                     this.point.value + '</b> items on <br><b>' + getPointCategoryName(this.point, 'y') + '</b>';
             }
         },
        
         series: [{
             name: 'Sales per employee',
             borderWidth: 1,
             data: [[11, 2, 1403]
     ,[11, 1, 1896]
     ,[11, 4, 1261]
     ,[11, 3, 5560]
     ,[16, 6, 413]
     ,[16, 5, 635]
     ,[1, 7, 700]
     ,[16, 7, 261]
     ,[1, 5, 1859]
     ,[10, 16, 675]
     ,[1, 6, 7878]
     ,[10, 14, 3310]
     ,[10, 15, 1508]
     ,[16, 9, 664]
     ,[16, 8, 6588]
     ,[16, 11, 894]
     ,[1, 12, 1238]
     ,[16, 10, 718]
     ,[1, 11, 2627]
     ,[16, 13, 1047]
     ,[1, 14, 8554]
     ,[16, 12, 846]
     ,[1, 13, 2678]
     ,[16, 15, 1497]
     ,[16, 14, 1054]
     ,[1, 15, 40113]
     ,[15, 16, 1643]
     ,[10, 10, 131580]
     ,[11, 0, 16964]
     ,[10, 11, 5607]
     ,[10, 12, 1586]
     ,[10, 13, 1052]
     ,[1, 8, 14358]
     ,[2, 0, 12774]
     ,[10, 8, 18670]
     ,[1, 10, 1746]
     ,[10, 9, 3706]
     ,[1, 9, 1759]
     ,[10, 7, 3589]
     ,[6, 16, 492]
     ,[16, 16, 57970]
     ,[15, 9, 2148]
     ,[1, 0, 18039]
     ,[15, 10, 2191]
     ,[15, 8, 16059]
     ,[15, 15, 255443]
     ,[15, 13, 3669]
     ,[15, 14, 8298]
     ,[10, 5, 7419]
     ,[15, 11, 3379]
     ,[10, 6, 593]
     ,[15, 12, 1833]
     ,[10, 4, 713]
     ,[7, 16, 200]
     ,[10, 1, 1355]
     ,[10, 2, 1354]
     ,[10, 3, 2889]
     ,[6, 10, 940]
     ,[6, 9, 814]
     ,[6, 12, 483]
     ,[6, 11, 1357]
     ,[6, 8, 5731]
     ,[15, 6, 5587]
     ,[15, 5, 2464]
     ,[1, 2, 4020]
     ,[6, 14, 7695]
     ,[1, 1, 292439]
     ,[6, 13, 773]
     ,[1, 4, 972]
     ,[1, 3, 2332]
     ,[6, 15, 6002]
     ,[15, 7, 732]
     ,[9, 16, 636]
     ,[7, 9, 444]
     ,[7, 8, 3873]
     ,[9, 13, 987]
     ,[9, 14, 2738]
     ,[9, 15, 1682]
     ,[6, 6, 84552]
     ,[7, 15, 449]
     ,[7, 14, 545]
     ,[7, 13, 396]
     ,[7, 12, 725]
     ,[6, 5, 716]
     ,[7, 11, 3843]
     ,[7, 10, 2973]
     ,[14, 16, 1038]
     ,[6, 7, 244]
     ,[15, 3, 2792]
     ,[9, 9, 129948]
     ,[10, 0, 12121]
     ,[15, 4, 1171]
     ,[9, 10, 3649]
     ,[15, 1, 39222]
     ,[9, 11, 2606]
     ,[15, 2, 6834]
     ,[9, 12, 1016]
     ,[9, 8, 22899]
     ,[7, 6, 142]
     ,[7, 5, 5105]
     ,[9, 7, 595]
     ,[7, 7, 25000]
     ,[5, 16, 670]
     ,[6, 4, 307]
     ,[14, 8, 18295]
     ,[6, 3, 866]
     ,[14, 9, 3045]
     ,[6, 2, 2434]
     ,[6, 1, 8416]
     ,[9, 6, 598]
     ,[14, 14, 196062]
     ,[14, 15, 7046]
     ,[14, 12, 1200]
     ,[14, 13, 1364]
     ,[15, 0, 15844]
     ,[14, 10, 3786]
     ,[9, 5, 1828]
     ,[14, 11, 3550]
     ,[9, 3, 3854]
     ,[9, 4, 585]
     ,[9, 1, 1496]
     ,[9, 2, 1205]
     ,[7, 3, 604]
     ,[5, 9, 1628]
     ,[7, 2, 361]
     ,[5, 8, 12980]
     ,[7, 1, 455]
     ,[5, 11, 12509]
     ,[6, 0, 6024]
     ,[5, 10, 7120]
     ,[14, 5, 3275]
     ,[14, 6, 5623]
     ,[4, 15, 994]
     ,[4, 14, 727]
     ,[5, 13, 1352]
     ,[5, 12, 3154]
     ,[5, 15, 1715]
     ,[7, 4, 358]
     ,[5, 14, 2453]
     ,[4, 9, 683]
     ,[4, 8, 8586]
     ,[8, 16, 7370]
     ,[14, 7, 810]
     ,[4, 13, 25773]
     ,[4, 12, 4429]
     ,[4, 11, 1624]
     ,[4, 10, 906]
     ,[8, 15, 10914]
     ,[8, 14, 14109]
     ,[8, 13, 10926]
     ,[8, 12, 11938]
     ,[14, 4, 666]
     ,[5, 6, 527]
     ,[5, 5, 120238]
     ,[7, 0, 3746]
     ,[13, 16, 944]
     ,[5, 7, 8373]
     ,[14, 2, 31740]
     ,[14, 3, 3064]
     ,[9, 0, 17014]
     ,[14, 1, 7977]
     ,[4, 16, 810]
     ,[8, 11, 29391]
     ,[8, 10, 18285]
     ,[8, 9, 23725]
     ,[8, 8, 1164252]
     ,[8, 7, 4416]
     ,[5, 3, 2084]
     ,[4, 3, 1679]
     ,[4, 2, 465]
     ,[5, 2, 1278]
     ,[4, 1, 992]
     ,[13, 8, 12694]
     ,[5, 4, 975]
     ,[5, 1, 1521]
     ,[4, 4, 124594]
     ,[13, 14, 1509]
     ,[13, 13, 147530]
     ,[13, 15, 3113]
     ,[3, 16, 1736]
     ,[13, 10, 1273]
     ,[13, 9, 1185]
     ,[8, 6, 3329]
     ,[13, 12, 4502]
     ,[14, 0, 14546]
     ,[8, 5, 10599]
     ,[13, 11, 2932]
     ,[8, 1, 9790]
     ,[8, 4, 6043]
     ,[8, 3, 50263]
     ,[8, 2, 7093]
     ,[5, 0, 12229]
     ,[13, 5, 1646]
     ,[4, 7, 577]
     ,[4, 6, 223]
     ,[13, 7, 549]
     ,[4, 5, 1195]
     ,[13, 6, 695]
     ,[0, 8, 248409]
     ,[0, 10, 11239]
     ,[0, 9, 17293]
     ,[13, 4, 27816]
     ,[0, 12, 10200]
     ,[13, 3, 2799]
     ,[0, 11, 16848]
     ,[0, 14, 11027]
     ,[0, 13, 9596]
     ,[3, 7, 753]
     ,[12, 16, 776]
     ,[3, 5, 2014]
     ,[0, 15, 10100]
     ,[13, 2, 735]
     ,[13, 1, 2600]
     ,[8, 0, 182995]
     ,[3, 6, 713]
     ,[0, 16, 7426]
     ,[3, 15, 2278]
     ,[3, 13, 2574]
     ,[3, 14, 2966]
     ,[12, 13, 4278]
     ,[3, 8, 50793]
     ,[12, 12, 154519]
     ,[12, 15, 1517]
     ,[12, 14, 1233]
     ,[12, 9, 1118]
     ,[3, 11, 6699]
     ,[12, 8, 14282]
     ,[3, 12, 2575]
     ,[4, 0, 9368]
     ,[3, 9, 4359]
     ,[12, 11, 5109]
     ,[13, 0, 11010]
     ,[3, 10, 3339]
     ,[12, 10, 1996]
     ,[2, 14, 37182]
     ,[2, 15, 6729]
     ,[2, 12, 784]
     ,[2, 13, 851]
     ,[0, 2, 7274]
     ,[0, 1, 11376]
     ,[0, 4, 6316]
     ,[0, 3, 33653]
     ,[12, 7, 1105]
     ,[3, 0, 28402]
     ,[12, 6, 468]
     ,[2, 10, 1916]
     ,[12, 5, 3726]
     ,[2, 11, 2145]
     ,[2, 8, 11760]
     ,[2, 9, 1514]
     ,[0, 5, 9650]
     ,[0, 6, 3600]
     ,[12, 3, 2752]
     ,[12, 2, 699]
     ,[0, 7, 4283]
     ,[12, 4, 4849]
     ,[3, 1, 1793]
     ,[3, 2, 1177]
     ,[11, 16, 858]
     ,[3, 3, 238183]
     ,[3, 4, 1217]
     ,[2, 16, 1098]
     ,[12, 1, 1136]
     ,[11, 15, 2419]
     ,[16, 0, 7272]
     ,[2, 1, 4441]
     ,[2, 4, 546]
     ,[2, 2, 179894]
     ,[2, 3, 1716]
     ,[11, 12, 4568]
     ,[11, 11, 162239]
     ,[11, 14, 2818]
     ,[1, 16, 2190]
     ,[11, 13, 2217]
     ,[11, 8, 29687]
     ,[12, 0, 12449]
     ,[11, 10, 5514]
     ,[11, 9, 2327]
     ,[0, 0, 809201]
     ,[2, 7, 622]
     ,[16, 1, 1964]
     ,[16, 3, 1433]
     ,[16, 2, 918]
     ,[16, 4, 769]
     ,[11, 7, 4391]
     ,[11, 5, 12593]
     ,[2, 5, 1896]
     ,[2, 6, 2460]
     ,[11, 6, 836]],
             dataLabels: {
                 enabled: true,
                 color: '#000000'
             }
         }],
        
         responsive: {
             rules: [{
                 condition: {
                     maxWidth: 500
                 },
                 chartOptions: {
                     yAxis: {
                         labels: {
                             formatter: function () {
                                 return this.value.charAt(0);
                             }
                         }
                     }
                 }
             }]
         }
        
     });
    
  • 결과

    Untitled

Leave a comment