본문 바로가기
Data Analysis/SQL Pandas

[Pandas] Example

by 베짱이28호 2024. 8. 29.

Pandas 공부 정리

LeetCode, Project 등 진행하면서 모르는 것들, 헷갈리는 것들, 유용한 팁 정리


1. unique()

Second Highest Salary - LeetCode

salary = list(employee['salary'].unique())
salary.sort(reverse=True)
  • 버전에 따라서 바로 sort가 되지 않는 경우도 있는데, sort_values를 사용해주면 된다.
  • 인자에는 ascending=True

2. rank()

Nth Highest Salary - LeetCode

employee['dense_rank'] = employee['salary'].rank(method='dense',ascending=False).astype(int)
  • SQL의 rank, dense rank처럼 등수를 매길 수 있다.
  • method를 통해서 등수 매기는 유형 처리 가능.
  • ascending을 통해서 오름차순, 내림차순 처리 가능.
  • 새로운 컬럼을 지정하면서 바로 astype으로 데이터 유형을 체크 가능하다.

3. empty

Nth Highest Salary - LeetCode

Nth_salary = employee[employee['dense_rank'] == N]['salary']

if Nth_salary.empty:
    Nth_salary = None
  • 데이터 프레임, 시리즈가 비어있는지 확인하기 위한 메서드
  • 파이썬 기본 자료형인 list, tuple처럼 not Nth_salary가 먹히지 않는다.
  • 비어있는지 확인이 필요하면 길이를 체크해줘도 되지만 가독성 좋게 empty를 써보자.

4. sort_values()

Rank Scores - LeetCode

scores.sort_values(by='rank', inplace=True)
  • 정렬은 자주 나오는 편이다.
  • 기준 없는 테이블에서는 새롭게 변수에 할당하지 않고 inplace를 통해서 데이터프레임 변경이 가능하다.
  • df.sort_values[by=[columns1,columns2], ascending=[True,False])같이 다중 컬럼에서 설정이 가능하다.

5. merge()

Department Highest Salary - LeetCode

answer = pd.merge(high_salary_employee, department,
                    left_on='departmentId', right_on='id',
                    suffixes=('_high_salary_employee', '_department'))
  • SQL의 join이랑 같은 역할이다.
  • pandas에도 join은 있지만 인덱스 기반이라서 merge를 더 많이 사용한다고 한다.
  • 컬럼명이 같은 경우 on을 통해서 join이 가능하다.
  • 컬럼명이 다른 경우 left on, right on을 통해서 어떤 컬럼을 기준으로 join하는지 명시해야한다.
  • suffixes같은 경우에는 join 이후에 컬럼명이 변하는데 어떤 테이블에서 왔는지 명시해주는 용도.

6. drop_duplicates()

Delete Duplicate Emails - LeetCode

import pandas as pd

def delete_duplicate_emails(person: pd.DataFrame) -> None:
    person.sort_values(by='id', inplace=True)
    person.drop_duplicates(subset='email', inplace=True)
  • drop duplicates의 파라미터에 keep='first'라는 메서드를 쓰면 속도가 현저히 향상되는 것을 확인했다.
  • 그냥 순회를 한 후 제거하는 방식보다는 명시적으로 지정해주면 효율적으로 처리하는 로직이 있는 듯

7. size(), count()

df.groupby('컬럼1').size()
df.groupby('컬럼1').count()
  • SQL의 count(*)와 count(컬럼)의 차이. Null을 count하냐 안하냐의 차이다.
  • 조건에 따라서 Null값을 카운팅 하는지, 안하는지 체크 후 사용하기.

8. filter()

import pandas as pd

def duplicate_emails(person: pd.DataFrame) -> pd.DataFrame:
    duplicated_emails = person.groupby('email').filter(lambda x: len(x) > 1)
    return pd.DataFrame({'Email':duplicated_emails['email'].unique()})
  • 컬럼에 조건 넣어줄 때 filter를 사용한다. groupby에 지정된 단일컬럼 email이 iterable한 객체로 들어온다.
  • 다중 컬럼을 지정한 경우 컬럼 명을 지정해야한다.
  • 불리언인덱싱보다 느리게 동작하는듯.

9. isin()

Customers Who Never Order - LeetCode

def find_customers(customers: pd.DataFrame, orders: pd.DataFrame) -> pd.DataFrame:
    answer = customers[~customers['id'].isin(orders['customerId'])][['name']]
    answer.rename(columns={'name': 'Customers'}, inplace=True)
    return answer
  • isin()의 파라미터로 들어갔으면 T, 아닌 경우 F로 반환을 한다.
  • 결과 값에 ~을 통해서 not in 역할을 할 수 있다.

10. shift()

Rising Temperature - LeetCode

def rising_temperature(weather: pd.DataFrame) -> pd.DataFrame:
    weather.sort_values(by='recordDate',inplace=True)
    weather['pre_temperature'] = weather['temperature'].shift(1)
    weather['pre_recordDate'] = weather['recordDate'].shift(1)
    result = weather[(weather['temperature'] > weather['pre_temperature']) & 
                     ((weather['recordDate'] - weather['pre_recordDate']) == timedelta(days=1))]
    return result[['id']]
  • shift를 통해서 이전 행의 값을 가져온다.
  • shift에 음수 인덱스를 가져오면 이후 행의 값을 가져올 수 있다.
  • 포인터를 그대로 두고 테이블을 한 칸 밑으로 이동시켜서 이전 값을 가져온다는 말.
  • 그냥 양수면 이전 값, 음수면 이후 값이라고 생각하자.

11. diff()

Rising Temperature - LeetCode

def rising_temperature(weather: pd.DataFrame) -> pd.DataFrame:
    weather.sort_values(by="recordDate", inplace=True)
    return weather[
        (weather.temperature.diff() > 0) & (weather.recordDate.diff().dt.days == 1)
    ][["id"]]
  • diff를 통해서 이전 값들과 연산을 바로 실행할 수 있다.
  • diff 인자는 기본적으로 1이 들어간다고 생각하면 된다.
  • shift와 비슷하게 양수면 이전 갑스, 음수면 이후 값이라고 생각하자.

12. pivot_table(), groupby().집계().unstack()

Trips and Users - LeetCode

import pandas as pd
from datetime import datetime, timedelta

def trips_and_users(trips: pd.DataFrame, users: pd.DataFrame) -> pd.DataFrame:

    window = trips[trips['request_at'].between('2013-10-01','2013-10-03')]
    unbanned = users[users['banned']=='No']

    join = pd.merge(window, unbanned, left_on='client_id', right_on='users_id')
    join = pd.merge(join, unbanned, left_on='driver_id', right_on='users_id')
    grouped = join.groupby(['request_at', 'status']).size().unstack(fill_value=0)
  • group화된 데이터프레임을 다룰 때, 세로로 stack된 형태를 우리가 평소에 생각하는 DataFrame 형태로 바꿔준다.
  • fill_value를 안넣으면 기존에 비어있는 부분은 Null로 채워진다.
  • pivot table과 비교하면, group화된 테이블에 전체 속성이 들어있지 않은 경우가 있는데, unstack을 사용해서 만드는 경우에는 아예 컬럼이 사라져버리는 문제가 발생한다.
  • 단순 두 컬럼 사이에서 pivot table을 만드는 경우 pivot table을 사용해야 추가적인 연산이 적어진다.

13. transform('집계')

Game Play Analysis IV - LeetCode

import pandas as pd

def gameplay_analysis(activity: pd.DataFrame) -> pd.DataFrame:

    activity.sort_values(by=['player_id', 'event_date'], inplace=True)
    activity['first_date'] = activity.groupby('player_id')['event_date'].transform('min')

    second = activity[activity['event_date'] == activity['first_date'] + timedelta(days=1)]
    return pd.DataFrame({'fraction':[round(len(second['player_id'].unique())/len(activity['player_id'].unique()),2)]})
  • groupby 이후에 집계함수를 쓰는 과정에서 min() vs transform('min')의 차이가 있다.
  • 전자는 시리즈 형태로 반환돼서 기존 데이터 프레임에 병합하게 되면 컬럼 수가 맞지 않을 수 있다.
  • 새 컬럼을 만들어서 기존 정보에 추가 정보를 만들어주고 싶으면 transform을 사용하자.

13. np.where

Triangle Judgement - LeetCode

import pandas as pd

def triangle_judgement(triangle: pd.DataFrame) -> pd.DataFrame:

    triangle['triangle'] = np.where((triangle['x'] + triangle['y'] <= triangle['z']) |
                                    (triangle['y'] + triangle['z'] <= triangle['x']) |
                                    (triangle['z'] + triangle['x'] <= triangle['y']),
                                    'No', 'Yes')
    return triangle

def triangle_judgement(triangle: pd.DataFrame) -> pd.DataFrame:

    triangle['triangle'] = np.where((triangle['x'] + triangle['y'] > triangle['z']) &
                                    (triangle['y'] + triangle['z'] > triangle['x']) &
                                    (triangle['z'] + triangle['x'] > triangle['y']),
                                    'Yes', 'No')
    return triangle
  • SQL의 IF와 같은 역할을 한다.
  • 주어진 조건이 참이면 앞의 값, 아니면 뒤의 값으로 반환한다.
  • 조건식에 다중조건을 넣어주면 연산하는데 오래 걸릴 수 있다.
  • AND가 여러개면 반대 조건으로 걸어주고 OR로 바꿔주면 시간 단축이 가능하다.

14. apply()

Triangle Judgement - LeetCode

import pandas as pd

def triangle_judgement(triangle: pd.DataFrame) -> pd.DataFrame:
    def check(row):
        x,y,z = row['x'],row['y'],row['z']
        if x+y>z and y+z>x and z+x>y:
            return 'Yes'
        return 'No'

    triangle['triangle'] = triangle.apply(check, axis=1)
    return triangle
  • 각 row를 입력으로 받아서 함수처리를 하고 값을 반환한다.
  • row 단위로 입력이 들어와서 iterrows()랑 시간은 크게 차이가 나지 않는 듯 하다.
  • 조건이 간단할 경우, lambda로 정의하고 간단하게 적용하는 것도 나쁘지 않아보인다.

댓글