아이디어는 다음과 같습니다.
대형 더미 CSV(100만 행)에 고객 데이터 샘플이 포함되어 있으며 아래 목표에 따라 처리를 수행합니다.
- CSV에서 데이터 추출
- 데이터/행 수 계산
- 도시별 고객 수를 그룹화
- 고객 수를 기준으로 도시를 높은 것부터 낮은 것 순으로 정렬
- 처리 시간 계산
고객의 샘플 CSV는 여기에서 다운로드할 수 있습니다 https://github.com/datablist/sample-csv-files
분명히 Go에는 CSV 처리를 위한 표준 lib가 있습니다. 더 이상 문제를 해결하기 위해 제3자 의존성이 필요하지 않습니다. 이는 좋은 일입니다. 따라서 해결책은 매우 간단합니다.
// open the file to a reader interface c, err := os.Open("../data/customers-1000000.csv") if err != nil { log.Fatal(err) } defer c.Close() // load file reader into csv reader // Need to set FieldsPerRecord to -1 to skip fields checking r := csv.NewReader(c) r.FieldsPerRecord = -1 r.ReuseRecord = true records, err := r.ReadAll() if err != nil { log.Fatal(err) }
FieldsPerRecord는 -1로 설정됩니다. 필드나 열 개수가 형식마다 다를 수 있으므로 행에서 필드 확인을 건너뛰고 싶기 때문입니다.
이 상태에서는 이미 csv의 모든 데이터를 로드하고 추출할 수 있으며 다음 처리 상태로 이동할 준비가 되어 있습니다. 또한 len(records) 함수를 사용하여 CSV의 행 수를 알 수 있습니다.
이제 기록을 반복하여 도시 이름과 총 고객이 다음과 같은 지도를 만들 수 있습니다.
["Jakarta": 10, "Bandung": 200, ...]
csv 행의 도시 데이터는 7번째 인덱스에 위치하며 코드는 다음과 같습니다
// create hashmap to populate city with total customers based on the csv data rows // hashmap will looks like be ["city name": 100, ...] m := map[string]int{} for i, record := range records { // skip header row if i == 0 { continue } if _, found := m[record[6]]; found { m[record[6]]++ } else { m[record[6]] = 1 } }
도시 지도가 없으면 새 지도를 만들고 총 고객 수를 1로 설정하세요. 그렇지 않으면 해당 도시의 총 수를 늘리세요.
이제 우리는 도시 컬렉션과 그 안에 있는 고객 수를 포함하는 지도 m을 갖게 되었습니다. 이 시점에서 우리는 각 도시의 고객 수를 그룹화하는 문제를 이미 해결했습니다.
표준 라이브러리에 지도를 정렬하는 기능이 있는지 찾아보았지만 아쉽게도 찾을 수 없었습니다. 인덱스 위치를 기준으로 데이터 순서를 재정렬할 수 있으므로 슬라이스에 대해서만 정렬이 가능합니다. 그럼, 현재 지도에서 한 조각을 만들어 보겠습니다.
// convert to slice first for sorting purposes dc := []CityDistribution{} for k, v := range m { dc = append(dc, CityDistribution{City: k, CustomerCount: v}) }
이제 CustomerCount를 기준으로 가장 높은 것에서 가장 낮은 것으로 정렬하는 방법은 무엇입니까? 이에 대한 가장 일반적인 알고리즘은 버블 쇼트를 사용하는 것입니다. 가장 빠르지는 않지만 작업을 수행할 수 있습니다.
버블 정렬은 인접한 요소의 순서가 잘못된 경우 반복적으로 교체하여 작동하는 가장 간단한 정렬 알고리즘입니다. 이 알고리즘은 평균 및 최악의 경우 시간 복잡도가 상당히 높기 때문에 대규모 데이터 세트에는 적합하지 않습니다.
참고: https://www.geeksforgeeks.org/bubble-sort-algorithm/
슬라이스를 사용하면 데이터를 반복하여 인덱스의 다음 값을 확인하고 현재 데이터가 다음 인덱스보다 작으면 교체합니다. 자세한 알고리즘은 참고사이트에서 확인하실 수 있습니다.
이제 정렬 과정은 다음과 같을 수 있습니다
// open the file to a reader interface c, err := os.Open("../data/customers-1000000.csv") if err != nil { log.Fatal(err) } defer c.Close() // load file reader into csv reader // Need to set FieldsPerRecord to -1 to skip fields checking r := csv.NewReader(c) r.FieldsPerRecord = -1 r.ReuseRecord = true records, err := r.ReadAll() if err != nil { log.Fatal(err) }
루프가 끝날 때쯤 최종 조각은 정렬된 데이터를 제공합니다.
처리 시간을 계산하는 것은 매우 간단합니다. 프로그램의 기본 프로세스를 실행하기 전과 후에 타임스탬프를 얻어 차이를 계산합니다. Go에서는 접근 방식이 충분히 간단해야 합니다.
["Jakarta": 10, "Bandung": 200, ...]
명령어로 프로그램 실행
// create hashmap to populate city with total customers based on the csv data rows // hashmap will looks like be ["city name": 100, ...] m := map[string]int{} for i, record := range records { // skip header row if i == 0 { continue } if _, found := m[record[6]]; found { m[record[6]]++ } else { m[record[6]] = 1 } }
행수, 정렬된 데이터, 처리 시간이 인쇄됩니다. 아래와 같습니다:
Go 성능 역시 100만행 csv를 1초 안에 처리!
완성된 모든 코드는 이미 내 Github 저장소에 게시되어 있습니다.
https://github.com/didikz/csv-processing/tree/main/golang
매핑할 CSV를 추출한 모든 레코드를 루프 처리했고 ReadAll() 소스에서 확인한 경우 지정된 파일 리더를 기반으로 슬라이스를 생성하는 루프도 있기 때문에 현재 솔루션이 더욱 최적화될 수 있다고 생각했습니다. 이로 인해 1 Mil 행은 1 Mil 데이터에 대해 2 x 루프를 생성할 수 있는데 이는 좋지 않습니다.
파일 리더에서 직접 데이터를 읽을 수 있다면 맵을 직접 생성할 수 있기 때문에 루프가 1개만 필요하다고 생각했습니다. 단, 레코드 조각은 다른 곳에서는 사용되지만 이 경우에는 사용되지 않습니다.
아직 알아낼 시간이 없지만 수동으로 할 경우 몇 가지 단점도 생각했습니다.
즐거운 코딩하세요!
위 내용은 Go를 사용한 대규모 CSV 처리의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!