Today I Learned

[내일배움캠프 QA/QC 트랙 6기 본캠프] TIL #014 🐼s

JiJi0406 2026. 5. 29. 21:03

001

오늘의 키워드: 너무 많아

어제는 Numpy, 오늘은 Pandas. 이틀 연속으로 데이터 분석의 핵심 라이브러리들을 알아보는 시간을 가졌다.

라이브러리... 혹은 패키지... 혹은 모듈 안에 들어있는 속성과 메서드들이 정말 셀 수 없이 많아서, 이걸 하나하나 다 외우는 건 절대 불가능!! 튜터님께서도 함수를 억지로 암기할 필요는 전혀 없고, 필요할 때마다 강의 자료나 구글링을 통해 찾아 쓰면 된다고 말씀해 주셨다. 중요한 건 무작정 외우는 게 아니라, 내가 원하는 결과를 내기 위해 어떤 도구를 어떤 목적으로 꺼내 써야 하는지 그 흐름을 파악하는 것인 듯하다. 😌 

 

 

요렇게... 보면서 푸는 게 맞겠지요

 


 

002

오늘 학습한 내용

 

[1] np.array()가 뭔데,

1. np.array가 존재하는 이유

파이썬 기본 내장 기능인 리스트[...]는 수학적 연산 기능이 X

리스트끼리 더하거나 곱하면 숫자가 계산되는 것이 아니라 단순히 리스트 자체가 복사되거나 연결이 됨

리스트끼리의 수학 연산을 좀 더 쉽게 하기 위해 존재하는 것이 넘파이(NumPy) 라이브러리의 np.array라는 것 !

  • 일반 리스트 연산: [1, 2, 3] * 2 → [1, 2, 3, 1, 2, 3] (단순 데이터 복사)
  • 넘파이 배열 연산: np.array([1, 2, 3]) * 2 →  [2, 4, 6] (리스트의 값끼리 서로 곱해짐)

 

2. ★ 1대1 매칭 연산 

기존 파이썬 리스트 구조에서 데이터 여러 개를 서로 계산하려면 for문 같은 반복문을 사용해 원소를 하나씩 꺼내며 연산해야 하는데, 그러면 코드가 참 복잡해진다.

 

but !! 넘파이 배열은 리스트 값끼리의 연산을 지원.

크기가 같은 배열끼리 연산 명령을 내리면, 넘파이가 내부적으로 각 배열의 동일한 인덱스(위치)에 있는 데이터끼리 자석처럼 1:1로 매칭하여 동시에 계산을 수행한다.

배열A = np.array([10, 20, 30])
배열B = np.array([2,  5,  10])

print(배열A / 배열B)
  • 내부 동작: [10/2, 20/5, 30/10]이 동시에 처리됨
  • 결과: [5.0, 4.0, 3.0]

굳이 반복문을 돌릴 필요 없이, 배열과 배열의 사칙연산 기호 한 줄만으로 모든 원소의 1:1 매칭 연산이 완결

  코드의 가독성 ↑ + 대용량 데이터 처리 속도 빨라짐!!

 

위에 내용 정리하고 나니까 실습문제 잘 풀렸다 👍

 

 

[2] Pandas 입문

1. Pandas가 뭔가요

파이썬에서 데이터 분석을 위해 사용하는 라이브러리.

엑셀 스프레드시트처럼 표 형태의 데이터를 다룰 수 있고, 전처리와 시각화할 때 거의 필수로 사용한다.

# 터미널
pip install pandas

# 코드블록
!pip install pandas

 

 

2. 데이터 구조 2가지

Series 1차원 인덱스 + 값으로 구성된 하나의 컬럼
DataFrame 2차원 여러 Series가 모인 표. 엑셀 시트랑 동일한 구조
# Series 생성 및 조작
ages = pd.Series([25, 30, 35, 28, 32])
ages * 2
ages[ages > 30]   # 조건 필터링 ([] 없으면 True/False만 나옴)
sum(ages)/len(ages)

# DataFrame은 딕셔너리 형태로 만드는 게 좋음
df = pd.DataFrame({
    'name': ['김철수', '이영희', '박민수'],
    'age': [25, 30, 35],
    'salary': [3500, 4200, 3800]
})

 

 

 

3. DataFrame 기본 확인 메서드

df.shape      # 데이터 크기 (행, 열)
df.columns    # 컬럼명 확인
df.dtypes     # 각 컬럼 데이터 타입
df.index      # 인덱스 정보
df.head()     # 상위 5행 (숫자 넣으면 그만큼)
df.tail()     # 하위 5행
df.info()     # 전체 정보 요약
df.describe() # 기본 통계 정보

 

▼ 컬럼명 바꾸기

df.columns = ['이름', '나이', '연봉']           # 전체 변경
df.rename(columns={'연봉': '급여'}, inplace=True) # 특정 컬럼만 변경

 

 

 

4. 파일 불러오기

df = pd.read_csv('파일경로/파일명.csv')   # CSV
df = pd.read_csv(url변수)               # URL 변수로도 가능
df = pd.read_clipboard()               # 클립보드에서 붙여넣기 (로컬 전용)
구분 절대경로 상대경로
기준점 컴퓨터 루트 디렉토리 (C:\ 또는 /) 현재 실행 중인 코드 파일의 위치 (.)
생김새 C:\Users\...\data.csv (긺) ./data/data.csv (짧음)
언제 씀? 내 컴퓨터 안에서만 대충 파일 긁어올 때 협업할 때

 

절대경로로 짜면 다른 사람 환경에서 파일이 불러와지지 않으니, 프로젝트할 때는 상대경로로 불러오기

 

 

5. 속성 vs 메서드

헷갈리기 쉬운 개념이라 정리함

속성 괄호 없음. 데이터의 상태/정보를 보여줌 df.shape, df.columns, df.dtypes
메서드 괄호 있음. 데이터에 특정 동작을 수행함 df.head(), df.info(), df.describe()

 

 

6. 데이터 선택 & 필터링

# 여러 컬럼 선택 (리스트 형태로 넣어야 함!)
df[['Name', 'Age', 'Sex']]

# 조건 필터링
titanic[titanic['Age'] > 30]

# 복합 조건
titanic[(titanic['Sex'] == 'female') & (titanic['Age'] < 30)]  # AND
titanic[(titanic['Sex'] == 'female') | (titanic['Age'] < 30)]  # OR

# 특정 값 포함 여부
titanic[titanic['Pclass'].isin([1, 2])]

# 결측치 필터링
titanic[titanic['Age'].notna()]   # 값 있는 것만
titanic[titanic['Age'].isna()]    # 결측치만

 

 

7. loc vs iloc 차이

loc 행/열 이름(Label) 포함
iloc 숫자 위치(Index) 제외 (파이썬 기본 문법과 동일)
titanic.loc[0]           # 0번 인덱스 행
titanic.iloc[0]          # 첫 번째 위치의 행
titanic[titanic['Age'] > 30].loc[0:4]  # 조건 + 범위

 

 

 

8. 기타 유용한 것들

# 새 컬럼 추가
titanic['Family_Size'] = titanic['SibSp'] + titanic['Parch'] + 1

# 다중 기준 정렬
titanic.sort_values(['Pclass', 'Age'], ascending=[True, False])

# 인덱스 초기화 / 특정 컬럼을 인덱스로 설정
df.reset_index()
df.set_index('Name')
# 인덱스는 중복값 없는 컬럼(주문번호, 사원번호 등)으로 잡는 게 좋음

 


003

오늘의 시행착오

 

반복문과 조건문 제어 (Feat. 개념 다시잡기...)

코드카타 23번

 

문제 설명
1937년 Collatz란 사람에 의해 제기된 이 추측은, 주어진 수가 1이 될 때까지 다음 작업을 반복하면, 모든 수를 1로 만들 수 있다는 추측입니다. 작업은 다음과 같습니다.

1-1. 입력된 수가 짝수라면 2로 나눕니다. 
1-2. 입력된 수가 홀수라면 3을 곱하고 1을 더합니다. 
2. 결과로 나온 수에 같은 작업을 1이 될 때까지 반복합니다. 


예를 들어, 주어진 수가 6이라면 6 → 3 → 10 → 5 → 16 → 8 → 4 → 2 → 1 이 되어 총 8번 만에 1이 됩니다. 위 작업을 몇 번이나 반복해야 하는지 반환하는 함수, solution을 완성해 주세요. 단, 주어진 수가 1인 경우에는 0을, 작업을 500번 반복할 때까지 1이 되지 않는다면 –1을 반환해 주세요.

 

.

.

.

 

흠... 예...

 

문제 자체는 이해함... 어려운 말은 없으니까. 뭘 구하고자 하는지도 알겠음... 근데 내가 생각한 걸 모두 코드로 구현하면 완전 뚱뚱 밤티밤티 코드 될 거 같기도 하고 실행이 잘 될 거라는 자신이 단 1도 없어서 이게 맞나 싶다가... 그냥 해봄.

 

일단 조건이 만족(주어진 수가 1이 됨)될 때까지 돌아야 하니까 while문을 써야겠다고 생각 (이건 잘했어. 유일하게.)

 

1차 시도: while문 조건의 오해와 변수 업데이트 누락

# 1차 코드 (실패)
def solution(num):
    answer = 0
    n = 0
    while n == 1:  # 문제점 1
        if num % 2 == 0:
            n = num / 2  # 문제점 2
            answer += 1
        # ... (생략)

답을 알고 나니까 참 말도 안 되는 코드인 게 보인다.

 

혼자 고민하다가 (한 3분?ㅋㅋ) 도저히 답이 안 나와서 제미나이한테 물어봤다. 이런 건 계속 째려보고 있는다고 해서 해결 안 된다.

 

 

바로 답 알려달라고 하면 공부가 안 되니 힌트를 달라고 함...^^

 

헷갈렸던 부분 & 배운 점

 

1. while문의 조건식은 '참(True)일 때' 실행된다.

  • 오해: 1이 될 때까지 돌려야지! 하고 == 1:로 짬
  • 해결: 목표에 도달하기 전까지 돌려야 하므로, != 1 (1이 아닌 동안)을 조건으로 잡아야 함.

 

While문은 거의 안 써봐서 아주 단.단.히 오해하고 있었음

 

 

2. 이전 단계의 결과물로 다음 단계를 계산하려면 기존 변수를 덮어씌워야 한다.

  • 오해: 입력값 num을 보존해야 할 것 같아서 새로운 변수 n에 계산 값을 담음. 이러면 num은 평생 숫자가 안 바뀌어서 무한 루프에 빠진다.
  • 해결: 작업 횟수는 answer가 세고 있으니, 숫자가 변경되는 걸 따라가기 위해 num = num / 2 처럼 변수 자체를 업데이트해야 함.

 

2차 시도: 조건문 순서 꼬임과 횟수 기준 착각

# 2차 코드 (시간 초과)
def solution(num):
    answer = 0
    while num != 1:
        if num % 2 == 0:
            num = num / 2
            answer += 1
        elif num % 2 == 1:
            num = num * 3 + 1
            answer += 1  
        elif num == 1:     # 문제점 1
            answer = 0
        elif num == 500:  # 문제점 2
            answer = -1

 

헷갈렸던 부분 & 배운 점

 

1. while num != 1: 안의 if num == 1:은 절대 실행되지 않는다.

  • while문 조건 자체에서 1이 아닌 애들만 들여보내 주기 때문에, 내부의 if num == 1은 실행될 일이 없는 죽은 코드. while문 들어가기 전에 answer = 0으로 선언해둬서 굳이 안 적어도 됨.

2. 제한 조건의 주체를 정확히 파악하자 (num vs answer)

  • 오해: 500번 반복을 제한해야 하는데 num > 500으로 계산 중인 '숫자' 크기를 제한해버림.(위에 조건들이 다 그래서 헷갈렸음) 
  • 해결: 횟수를 세고 있는 answer >= 500으로 조건을 고쳐야 함.

 

3차 시도: 값 변경과 함수의 종료 (return) 시점 오류

# 3차 코드 (기댓값과 다른 뜬금없는 양수 반환)
def solution(num):
    answer = 0
    while num != 1:
        if num % 2 == 0:
            num = num / 2
            answer += 1
        elif num % 2 == 1:
            num = num * 3 + 1
            answer += 1
        elif answer == 500:
            answer = -1  # 문제점

헷갈렸던 부분 & 배운 점

  • 값만 바꾸면 코드가 멈추지 않고, 변수가 오염된 채 계산이 이어진다...
    • 오해: answer가 500이 되면  -1을 내놓고 그대로 루프가 끝날  줄 알았음. 하지만 뜬금없는 양수 값이 반환되는 현상이 발생
    • 원인 분석: answer가 500이 되면 -1로 바뀌지만, while문 탈출 X (num은 여전히 1이 아니니까) 다음 바퀴에서는  answer == 500 조건이 거짓이 되어 다시 위의 조건문으로 넘어가 버림... 결국 num 계산 재개하면서  -1이었던 answer가 다시 0, 1, 2...로 증가하며 카운트가 리셋됨. 이 상태로 num이 1이 될 때까지 끝까지 계산한 뒤 탈출하므로 기댓값(-1)과 전혀 다른 뜬금없는 양수가 반환된 것
    • 해결: 특정 조건이 만족되었을 때 계산을 즉시 중단하고 싶다면, 변수 값만 바꿀 게 아니라 return -1을 사용해 함수 전체를 그 즉시 종료하고 빠져나와야 함. (그리고 이때 while문 안에 if문을 독립적으로 여러 개 쓸 수 있다는 것도 새로 배움!)

 

최종 정답 코드 & 회고

def solution(num):
    answer = 0
    while num != 1:
        # 짝수/홀수 계산하는 if문 한 개
        if num % 2 == 0:
            num = num / 2
            answer += 1
        elif num % 2 == 1:
            num = num * 3 + 1
            answer += 1
            
        # 500번째가 되었을 때 탈출하는 if문 한 개 (독립된 if문 2개 사용 가능!)
        if answer == 500:
            return -1

    return answer

 

 

while 조건문의 참/거짓 개념, 루프 안에서 변수를 지속해서 업데이트해야 하는 이유, 그리고 return을 활용한 제어문 탈출 타이밍까지 배우게 된 문제였다... 하이고 벅차라. 주말 보내고 오면 까먹을 거 같은데 ^^ 위에 정리해둔 내용 눈에 익을 정도로 조금씩 봐야겠다. 열심히 쓴 의미가 있도록...

 

 

빨리 레벨업하고 전직합시다