프로그래밍 초보자를 위한 np.where 완벽 가이드: 조건에 맞는 데이터 찾기 mymaster, 2024년 06월 30일 파이썬으로 데이터 분석 작업을 하다 보면 특정 조건에 맞는 데이터만 추출해야 하는 경우가 종종 생깁니다. 예를 들어, 설문 조사 데이터에서 특정 나이대의 응답자만 따로 분석하거나, 특정 기준 이상의 매출을 달성한 제품 목록을 만들고 싶을 수 있습니다. 이때 np.where 함수는 강력한 도구가 됩니다. 본 글에서는 프로그래밍 초보자도 쉽게 이해할 수 있도록 np.where 함수의 기본 개념부터 다양한 활용 방법까지 자세히 알아보겠습니다. np.where 함수를 마스터하면 복잡한 데이터 처리 작업을 간결하고 효율적으로 수행할 수 있게 될 것입니다. 1. NumPy 라이브러리와 배열 이해하기 np.where 함수를 본격적으로 살펴보기 전에, 먼저 NumPy 라이브러리와 배열에 대한 기본적인 이해가 필요합니다. 1.1. NumPy 라이브러리란? NumPy는 ‘Numerical Python’의 약자로, 파이썬에서 과학 계산을 위한 핵심 라이브러리입니다. NumPy는 고성능의 다차원 배열 객체와 이러한 배열을 처리하기 위한 다양한 함수를 제공합니다. 특히, NumPy는 벡터 및 행렬 연산에 매우 효율적이며, 데이터 분석, 머신 러닝, 이미지 처리 등 다양한 분야에서 널리 사용됩니다. 1.2. 배열(Array)이란? 배열은 동일한 데이터 유형의 값들이 메모리에 연속적으로 저장되는 데이터 구조입니다. NumPy의 배열은 ndarray 객체로 표현되며, 1차원부터 다차원까지 다양한 형태로 생성될 수 있습니다. 예를 들어, 1부터 10까지의 숫자를 담고 있는 1차원 배열은 다음과 같이 생성할 수 있습니다. import numpy as np arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) 1.3. NumPy 라이브러리 설치 및 import np.where 함수를 사용하려면 먼저 NumPy 라이브러리를 설치해야 합니다. 파이썬 환경에서 다음 명령어를 사용하여 NumPy를 설치할 수 있습니다. pip install numpy NumPy 라이브러리 설치 후에는 파이썬 스크립트에서 import 문을 사용하여 NumPy를 불러와야 합니다. 일반적으로 np라는 별칭으로 NumPy를 불러옵니다. import numpy as np 2. np.where 함수 기본 구조 np.where 함수는 NumPy 배열에서 조건에 맞는 요소의 인덱스를 찾아 반환합니다. 즉, 배열에서 특정 조건을 만족하는 요소들이 어디에 위치하는지 알려줍니다. np.where 함수의 기본적인 구조는 다음과 같습니다. numpy.where(condition, [x, y]) condition: 배열의 각 요소에 대해 평가할 조건식입니다. True 또는 False 값으로 평가됩니다. x: 조건식이 True인 경우 반환할 값입니다. 생략 가능합니다. y: 조건식이 False인 경우 반환할 값입니다. 생략 가능합니다. 2.1. np.where 함수 동작 방식 조건식 평가: np.where 함수는 먼저 입력 배열의 각 요소에 대해 지정된 condition을 평가합니다. 인덱스 반환: 조건식을 만족하는 요소, 즉 True로 평가된 요소의 인덱스를 찾아 새로운 배열로 반환합니다. 값 치환 (선택 사항): 만약 x와 y 인자가 함께 제공된 경우, np.where 함수는 조건식이 True인 경우 해당 인덱스 위치에 x 값을, False인 경우 y 값을 사용하여 새로운 배열을 생성합니다. 2.2. 간단한 예시 다음 예시는 np.where 함수의 기본적인 동작 방식을 보여줍니다. import numpy as np arr = np.array([1, 5, 3, 8, 2, 6]) # 짝수인 요소의 인덱스 찾기 even_indices = np.where(arr % 2 == 0) print(even_indices) # 출력: (array([0, 3, 5]),) 이 코드는 다음과 같이 동작합니다. arr % 2 == 0 조건식은 배열 arr의 각 요소가 짝수인지 여부를 확인합니다. np.where 함수는 조건식이 True인 요소의 인덱스를 찾아 even_indices 변수에 저장합니다. 출력 결과인 (array([0, 3, 5]),)는 배열 arr에서 0번째, 3번째, 5번째 요소가 짝수임을 나타냅니다. 3. np.where 함수 활용 방법: 다양한 조건 적용하기 np.where 함수는 단순히 특정 값과 같은 요소를 찾는 것 외에도 다양한 조건을 적용하여 원하는 데이터를 추출하는 데 활용될 수 있습니다. 3.1. 특정 값보다 큰/작은 요소 찾기 np.where 함수를 사용하여 배열에서 특정 값보다 큰 또는 작은 요소의 인덱스를 찾을 수 있습니다. 예시: 5보다 큰 요소 찾기 import numpy as np arr = np.array([1, 7, 3, 9, 2, 6]) # 5보다 큰 요소의 인덱스 찾기 indices_greater_than_5 = np.where(arr > 5) print(indices_greater_than_5) # 출력: (array([1, 3, 5]),) # 5보다 큰 요소만 새로운 배열로 추출 greater_than_5 = arr[indices_greater_than_5] print(greater_than_5) # 출력: [7 9 6] 3.2. 여러 조건 결합하기 논리 연산자 (&, |, ~)를 사용하여 여러 조건을 결합할 수 있습니다. 예시: 짝수이면서 5보다 작은 요소 찾기 import numpy as np arr = np.array([1, 6, 4, 9, 2, 7]) # 짝수이면서 5보다 작은 요소의 인덱스 찾기 indices_even_and_less_than_5 = np.where((arr % 2 == 0) & (arr < 5)) print(indices_even_and_less_than_5) # 출력: (array([2, 4]),) # 짝수이면서 5보다 작은 요소만 새로운 배열로 추출 even_and_less_than_5 = arr[indices_even_and_less_than_5] print(even_and_less_than_5) # 출력: [4 2] 주의 사항: 파이썬의 일반적인 논리 연산자인 and, or, not은 NumPy 배열에서는 사용할 수 없습니다. 대신 &, |, ~ 연산자를 사용해야 합니다. 3.3. 조건에 따라 값 바꾸기 np.where 함수에 x와 y 인자를 함께 전달하면 조건에 따라 배열의 값을 변경할 수 있습니다. 예시: 5보다 큰 요소는 ‘크다’, 작거나 같은 요소는 ‘작거나 같다’로 변경 import numpy as np arr = np.array([1, 7, 3, 9, 2, 6]) # 조건에 따라 값 바꾸기 new_arr = np.where(arr > 5, '크다', '작거나 같다') print(new_arr) # 출력: ['작거나 같다' '크다' '작거나 같다' '크다' '작거나 같다' '크다'] 4. np.where 함수 심화 활용: 다차원 배열 다루기 np.where 함수는 2차원 이상의 다차원 배열에서도 동일한 방식으로 작동합니다. 다만, 반환되는 인덱스는 각 차원별로 구분되어 튜플 형태로 제공됩니다. 4.1. 2차원 배열에서 조건에 맞는 요소 찾기 예시: 2차원 배열에서 5보다 큰 요소의 인덱스 찾기 import numpy as np arr_2d = np.array([[1, 6, 3], [7, 2, 8], [4, 9, 5]]) # 5보다 큰 요소의 인덱스 찾기 row_indices, col_indices = np.where(arr_2d > 5) print(row_indices) # 출력: [0 1 1 2] print(col_indices) # 출력: [1 0 2 1] 출력 결과에서 row_indices는 조건을 만족하는 요소의 행 인덱스를, col_indices는 열 인덱스를 나타냅니다. 예를 들어, (0, 1)은 0번째 행, 1번째 열의 요소를 의미하며, 이는 6입니다. 4.2. 다차원 배열에서 조건에 따라 값 바꾸기 예시: 2차원 배열에서 5보다 큰 요소는 1, 작거나 같은 요소는 0으로 변경 import numpy as np arr_2d = np.array([[1, 6, 3], [7, 2, 8], [4, 9, 5]]) # 조건에 따라 값 바꾸기 new_arr_2d = np.where(arr_2d > 5, 1, 0) print(new_arr_2d) # 출력: # [[0 1 0] # [1 0 1] # [0 1 0]] 5. np.where 함수 활용 팁: 성능 향상 및 추가 기능 np.where 함수를 더욱 효율적으로 사용하고, 다양한 기능을 활용하기 위한 몇 가지 팁을 소개합니다. 5.1. np.where 함수와 팬시 인덱싱 결합하기 np.where 함수를 사용하여 얻은 인덱스를 팬시 인덱싱과 함께 사용하면 더욱 복잡한 데이터 처리 작업을 수행할 수 있습니다. 예시: 짝수 행의 5보다 큰 요소만 추출하기 import numpy as np arr_2d = np.array([[1, 6, 3], [7, 2, 8], [4, 9, 5]]) # 짝수 행의 인덱스 even_row_indices = np.where(np.arange(arr_2d.shape[0]) % 2 == 0)[0] print(even_row_indices) # 출력: [0 2] # 짝수 행에서 5보다 큰 요소의 인덱스 row_indices, col_indices = np.where(arr_2d[even_row_indices] > 5) print(row_indices) # 출력: [0 1] print(col_indices) # 출력: [1 1] # 짝수 행의 5보다 큰 요소 추출 result = arr_2d[even_row_indices][row_indices, col_indices] print(result) # 출력: [6 9] 5.2. np.where 함수를 사용한 조건부 집계 np.where 함수를 np.sum, np.mean 등 다른 NumPy 함수와 함께 사용하면 조건에 맞는 요소들에 대한 집계 연산을 수행할 수 있습니다. 예시: 5보다 큰 요소들의 합 구하기 import numpy as np arr = np.array([1, 7, 3, 9, 2, 6]) # 5보다 큰 요소들의 합 sum_greater_than_5 = np.sum(np.where(arr > 5, arr, 0)) print(sum_greater_than_5) # 출력: 22 이 코드는 np.where 함수를 사용하여 5보다 큰 요소는 그대로 유지하고, 그렇지 않은 요소는 0으로 바꾼 후 np.sum 함수를 사용하여 합계를 계산합니다. 5.3. np.where 함수 성능 고려 사항 np.where 함수는 매우 강력하고 유용하지만, 대량의 데이터를 처리할 때는 성능 저하가 발생할 수 있습니다. 성능을 향상시키기 위해 다음과 같은 방법을 고려할 수 있습니다. 벡터 연산 활용: np.where 함수 대신 벡터 연산을 사용하면 더 빠른 속도로 동일한 결과를 얻을 수 있습니다. 예를 들어, 5보다 큰 요소를 찾는 대신 arr[arr > 5]와 같이 불리언 인덱싱을 사용할 수 있습니다. 반복문 최소화: 가능하면 np.where 함수 내에서 반복문을 사용하지 않도록 코드를 작성하는 것이 좋습니다. 적절한 데이터 타입 사용: NumPy 배열의 데이터 타입을 적절하게 설정하면 메모리 사용량을 줄이고 연산 속도를 높일 수 있습니다. 6. np.where 함수 활용 사례: 실제 데이터 분석 문제 해결 np.where 함수는 다양한 데이터 분석 문제를 해결하는 데 유용하게 활용될 수 있습니다. 몇 가지 실제 사례를 통해 np.where 함수의 활용 방법을 더 자세히 알아보겠습니다. 6.1. 결측값 처리 데이터 분석에서 결측값 처리는 매우 중요한 작업 중 하나입니다. np.where 함수를 사용하면 특정 조건을 만족하는 결측값을 원하는 값으로 쉽게 대체할 수 있습니다. 예시: NumPy 배열에서 특정 값을 결측값으로 처리하고, 평균값으로 대체하기 import numpy as np # 예시 데이터: 0을 결측값으로 간주 data = np.array([10, 5, 0, 8, 0, 6]) # 결측값 (0)의 위치 찾기 missing_indices = np.where(data == 0) # 결측값이 아닌 데이터의 평균 계산 mean_value = np.mean(data[data != 0]) # 결측값을 평균값으로 대체 data[missing_indices] = mean_value print(data) # 출력: [10. 5. 7.2 8. 7.2 6. ] 6.2. 이상값 처리 이상값은 데이터 분석 결과에 큰 영향을 미칠 수 있으므로 적절하게 처리해야 합니다. np.where 함수를 사용하면 특정 조건을 만족하는 이상값을 쉽게 찾아 제거하거나 다른 값으로 대체할 수 있습니다. 예시: 데이터에서 1.5 * IQR (Interquartile Range) 이상 벗어난 값을 이상값으로 처리하고, 상한/하한값으로 대체하기 import numpy as np data = np.array([10, 15, 12, 8, 100, 14, 9]) # 1사분위수와 3사분위수 계산 q1, q3 = np.percentile(data, [25, 75]) # IQR 계산 iqr = q3 - q1 # 상한/하한값 계산 upper_bound = q3 + 1.5 * iqr lower_bound = q1 - 1.5 * iqr # 이상값을 상한/하한값으로 대체 data = np.where(data > upper_bound, upper_bound, data) data = np.where(data < lower_bound, lower_bound, data) print(data) # 출력: [10. 15. 12. 8. 24.5 14. 9. ] 6.3. 구간 나누기 데이터를 특정 기준에 따라 여러 구간으로 나누어 분석해야 하는 경우에도 np.where 함수를 활용할 수 있습니다. 예시: 점수 구간에 따라 등급 매기기 import numpy as np scores = np.array([95, 82, 75, 60, 90, 88, 70]) # 등급 기준 설정 grade_boundaries = [60, 70, 80, 90, 100] grades = ['F', 'D', 'C', 'B', 'A'] # 점수에 따라 등급 배열 생성 student_grades = [] for i in range(len(grade_boundaries) - 1): student_grades.append(np.where((scores >= grade_boundaries[i]) & (scores < grade_boundaries[i + 1]), grades[i], np.nan)) # NaN 값을 제거하고 등급 출력 final_grades = np.array(student_grades).astype('str').T final_grades = np.where(final_grades == 'nan', '', final_grades) print(final_grades) # 출력: [['A'] ['C'] ['C'] ['F'] ['A'] ['B'] ['D']] 7. 결론 이번 글에서는 파이썬 NumPy 라이브러리의 np.where 함수에 대해 자세히 알아보았습니다. np.where 함수를 사용하면 배열에서 특정 조건을 만족하는 요소의 인덱스를 찾고, 이를 활용하여 다양한 데이터 처리 작업을 수행할 수 있습니다. 조건에 맞는 값 추출, 값 변경, 결측값 처리, 이상값 처리, 구간 나누기 등 데이터 분석에서 자주 등장하는 문제들을 np.where 함수를 사용하여 효과적으로 해결할 수 있습니다. np.where 함수는 간결하고 직관적인 문법으로 복잡한 데이터 조작 작업을 효율적으로 수행할 수 있도록 도와줍니다. 데이터 분석을 위한 NumPy 라이브러리를 학습하는 과정에서 np.where 함수를 능숙하게 활용한다면 데이터 분석 능력을 한 단계 더 발전시킬 수 있을 것입니다. 목차 Toggle 1. NumPy 라이브러리와 배열 이해하기2. np.where 함수 기본 구조3. np.where 함수 활용 방법: 다양한 조건 적용하기4. np.where 함수 심화 활용: 다차원 배열 다루기5. np.where 함수 활용 팁: 성능 향상 및 추가 기능6. np.where 함수 활용 사례: 실제 데이터 분석 문제 해결7. 결론 post