📘 대용량 데이터 처리 프로젝트
👉 인구이동 데이터로 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하는 기능
-
전입, 전출 코드 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()); }
-
전입, 전출 코드 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()); }
-
인터페이스 사용하여 parsing 메서드 구현
3. 파일로 저장하고 Parsing
👉 필요한 데이터만 parsing해서 파일로 저장하는 이유
500mb를 매번 읽는 것은 부담
왜 부담이냐? 한번 실행 할 때 매번 500mb씩 파싱을 하기 때문
→ 파일을 메모리로 로드하는데 10초가량 걸리기 때문
-
파일 생성하는
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"); }
-
파일에 저장하는
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"); }
-
시/도 코드와 시/도 명 매핑
4. 파일 데이터로 데이터 분석
👉 Map으로 어디로 많이 갔는지 개수 세기
-
전출 → 전입과 횟수 구하는 메서드
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); }
-
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); }
-
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); } } } } }] } });
-
결과
Leave a comment