Do it! 판다스 입문

Chapter 07-3~5 여러 열을 하나로 정리하기, 중복 데이터 처리하기, 대용량 데이터 처리하기

판다조아 2022. 6. 22. 20:27

여러 열을 하나로 정리하기

보통 데이터프레임의 열은 파이썬의 변수와 같은 개념으로 사용합니다. 하지만 비슷한 성질의 데이터를 관리하기 위해 열이 여러 개로 분리되어 있는 경우도 있습니다. 다음 실습을 통해 여러 개로 분리된 열을 정리하는 방법에 대해 알아보겠습니다.

 

 

기상 데이터의 여러 열을 하나로 정리하기 - melt, pibot_table 메서드

1. 다음은 기상 데이터를 불러와 출력한 것입니다. 날짜 열(d1, ..., d31)에는 각 월별 최고, 최저 온도 데이터가 저장되어 있습니다. 지금은 날짜 열이 옆으로 길게 늘어져 있어 보기 불편합니다. 먼저 날짜 열을 행 데이터로 피벗하겠습니다.

weather = pd.read_csv('../data/weather.csv')
print(weather.iloc[:5, :])

                  id      year    month   element       d1       d2       d3       d4       d5       d6        d7 ...
0    MX17004    2010            1        tmax    NaN    NaN    NaN    NaN    NaN    NaN    NaN
1    MX17004    2010            1         tmin    NaN    NaN    NaN    NaN    NaN    NaN    NaN
2    MX17004    2010            2        tmax    NaN    27.3     24.1   NaN    NaN    NaN    NaN
3    MX17004    2010            2         tmin    NaN    14.4     14.4   NaN    NaN    NaN    NaN
4    MX17004    2010            3        tmax    NaN    NaN    NaN    NaN    32.1    NaN    NaN

 

2. 다음은 melt 메서드로 일별 온도 측정값(d1, d2, ...)을 피벗한 것입니다. 그러면 day 열에 날짜 열이 정리되고 날짜 열의 데이터는 temp 열에 정리됩니다. 하지만 아직 최고, 최저 온도가 한눈에 잘 들어오지 않습니다.

weather_melt = pd.melt(weather, id_vars=['id', 'year', 'month', 'element'], var_name='day', value_name='temp')
print(weather_melt.head( ))

                  id      year    month   element   day   temp
0    MX17004    2010            1        tmax     d1    NaN
1    MX17004    2010            1         tmin     d1    NaN
2    MX17004    2010            2        tmax     d1    NaN
3    MX17004    2010            2         tmin     d1    NaN
4    MX17004    2010            3        tmax     d1    NaN

 

3. 이제 pivot_table 메서드를 사용할 차례입니다. pivot_table 메서드는 행과 열의 위치를 다시 바꿔 정리해 줍니다. index 인자에는 위치를 그대로 유지할 열 이름을 지정하고, columns 인자에는 피벗할 열 이름을 지정하고, values 인자에는 새로운 열의 데이터가 될 열 이름을 지정하면 됩니다. 다음은 pivot_table 메서드로 행과 열의 위치를 다시 바꾼 것입니다. 과정 2의 실행 결과를 비교하여 살펴보세요.

weather_tidy = weather_melt.pivot_table(
    index=['id', 'year', 'month', 'day'],
    columns='element',
    values='temp',
    dropna=False)
print(weather_tidy)

element                                   tmax   tmin
id                year  month  day 
MX17004  2010  1          d1     NaN   NaN
                                       d10   NaN   NaN
...                                               ...       ...
                            12       d10   NaN   NaN
                                       d11   NaN   NaN
...
                                        d7    NaN   NaN
                                        d8    NaN   NaN
                                        d9    NaN   NaN

[321 rows x 2 columns]

 

4. 다음은 과정 3에서 구한 데이터프레임의 인덱스를 reset_index 메서드로 새로 지정한 것입니다.

weather_tidy_flat = weather_tidy.reset_index( )

print(weather_tidy_flat.head( ))

element                 id      year    month   day   tmax    tmin
0               MX17004    2010            1      d1    NaN   NaN
1               MX17004    2010            1    d10    NaN   NaN
2               MX17004    2010            1    d11    NaN   NaN
3               MX17004    2010            1    d12    NaN   NaN
4               MX17004    2010            1    d13    NaN   NaN

 

 

 

 

 

중복 데이터 처리하기

이번에 다룰 빌보드 차트 데이터는 artist, track, time, date.entered 열의 데이터가 반복됩니다. 이런 반복되는 데이터는 따로 관리하는 것이 좋습니다. 왜 따로 관리하는 것이 좋을까요? 그리고 따로 관리하려면 어떻게 해야 할까요? 다음 실습을 통해 중복 데이터를 처리하는 방법에 대해 알아보겠습니다.

 

 

빌보드 차트의 중복 데이터 처리하기

1. 다음은 빌보드 차트 데이터를 불러온 것입니다.

billboard = pd.read_csv('../data/billboard.csv')
billboard_long = pd.melt(billboard, id_vars=['year', 'artist', 'track', 'time', 'date.entered'], var_name='week', value_name='rating')

print(billboard_long.shape)

(24092, 7)

print(billboard_long.head( ))

       year                  artist                                  track     time  date.entered    week   rating
0    2000                  2Pac    Baby Don't Cry (Keep...    4:22    2000-02-26      wk1     87.0
1    2000            2Ge+her      The Hardest Part of ...    3:15    2000-09-02      wk1     91.0
2    2000    3 Doors Down                        Kryptonite    3:53    2000-04-08       wk1     81.0
3    2000    3 Doors Down                               Loser    4:24    2000-10-21       wk1     76.0
4    2000            504 Boyz               Wobble Wobble    3:35    2000-04-15       wk1     57.0

 

2. 노래 제목(track)이 Loser인 데이터만 따로 모아 살펴보면 중복 데이터가 꽤 많다는 것을 알 수 있습니다. 예를 들어 가수(artist)는 고유한 값이기 때문에 따로 관리하는 것이 데이터의 일관성을 유지하는 데 더 도움이 됩니다.

print(billboard_long[billboard_long.track == 'Loser'].head( ))

            year                  artist      track     time  date.entered    week   rating
3         2000    3 Doors Down    Loser    4:24    2000-10-21       wk1     76.0
320     2000    3 Doors Down    Loser    4:24    2000-10-21       wk2     76.0
637     2000    3 Doors Down    Loser    4:24    2000-10-21       wk3     72.0
954     2000    3 Doors Down    Loser    4:24    2000-10-21       wk4     69.0
1271   2000    3 Doors Down    Loser    4:24    2000-10-21       wk5     67.0

 

3. 중복 데이터를 가지고 있는 열은 year, artist, track, time, date입니다. 이 열을 따로 모아 새로운 데이터프레임에 저장합니다.

billboard_songs = billboard_long[['year', 'artist', 'track', 'time']]
print(billboard_songs.shape)

(24092, 4)

 

4. 그런 다음 drop_duplicates 메서드로 데이터프레임의 중복 데이터를 제거합니다.

billboard_songs = billboard_songs.drop_duplicates( )
print(billboard_songs.shape)

(317, 4)

 

5. 중복을 제거한 데이터프레임에 다음과 같이 아이디(id)도 추가합니다.

billboard_songs['id'] = range(len(billboard_songs))
print(billboard_songs.head(n=10))

       year                     artist                                   track     time    id
0    2000                     2Pac     Baby Don't Cry (Keep...    4:22     0
1    2000               2Ge+her       The Hardest Part of ...    3:15     1
2    2000       3 Doors Down                         Kryptonite    3:53      2
3    2000       3 Doors Down                                Loser    4:24      3
4    2000               504 Boyz                Wobble Wobble    3:35      4
5    2000                      98^0   Give Me Just One Nig...    3:24      5
6    2000                A*Teens                Dancing Queen     3:44      6
7    2000                  Aaliyah                   I Don't Wanna    4:15      7
8    2000                  Aaliyah                          Try Again     4:03     8
9    2000    Adams, Yolanda                 Open My Heart    5:30      9

 

6. 다음은 merge 메서드를 사용해 노래 정보와 주간 수위 데이터를 합친 것입니다.

billboard_ratings = billboard_long.merge(billboard_songs, on=['year', 'artist', 'track', 'time'])
print(billboard_ratings.shape)

(2409, 8)

print(billboard_ratings.head( ))

       year    artist                                  track     time  date.entered    week   rating    id
0    2000    2Pac    Baby Don't Cry (Keep...    4:22    2000-02-26      wk1     87.0     0
1    2000    2Pac    Baby Don't Cry (Keep...    4:22    2000-02-26      wk2     82.0     0
2    2000    2Pac    Baby Don't Cry (Keep...    4:22    2000-02-26      wk3     72.0     0
3    2000    2Pac    Baby Don't Cry (Keep...    4:22    2000-02-26      wk4     77.0     0
4    2000    2Pac    Baby Don't Cry (Keep...    4:22    2000-02-26      wk5     87.0     0

 

 

 

 

 

대용량 데이터 처리하기

여러 개로 나누어진 데이터 불러오기

데이터는 필요에 따라 나누어 저장하기도 합니다. 데이터를 나누어 저장하면 용량이 작아져 데이터를 저장하거나 다른 사람에게 공유할 때 유용하죠. 또 어떤 경우에는 처음부터 크기가 작은 데이터가 생성되는 경우도 있습니다. 예를 들어 주식 정보를 매일 수집한다면 일 단위로 데이터가 생성됩니다. 지금까지는 한 덩어리의 데이터를 불러와 여러 가지 실습을 진행했습니다. 그러면 여러 개로 나누어진 데이터를 신속하게 불러오려면 어떻게 해야 할까요? 그리고 이런 데이터를 다루는 방법은 무엇일까요?

 

 

뉴욕 택시 데이터 준비하기

뉴욕 택시 데이터는 13억 대의 뉴욕 택시에 대한 정보를 가지고 있습니다. 파일의 개수도 140개나 됩니다. 이번 실습에서는 이 중 5개의 데이터만 사용해 보겠습니다.

 

1. 다음을 입력하여 실행한 후 데이터를 내려받습니다. 네트워크 상태에 따라 5 ~ 15분이 소요됩니다.

import os 
import urllib.request

with open('../data/raw_data_urls.txt', 'r') as data_urls:
    for line, url in enumerate(data_urls):
        if line == 5:
            break 
        fn = url.split('/')[-1].strip()
        fp = os.path.join('', '../data', fn)
        print(url)
        print(fp)
        urllib.request.urlretrieve(url, fp)

 

2. 내려받은 데이터는 data 폴더에 'fhv_tripdata_YYYY_MM.csv'라는 이름으로 저장됩니다. 이제 판다스로 데이터를 불러오면 됩니다. glob 라이브러리에 포함된 glob 메서드는 특정한 패턴의 이름을 가진 파일을 한 번에 읽어 들일 수 있습니다. glob 메서드로 5개의 파일을 불러옵니다.

import glob 
nyc_taxi_data = glob.glob('../data/fhv_*') 
print(nyc_taxi_data)

['../data\\fhv_tripdata_2015-01.csv',
'../data\\fhv_tripdata_2015-02.csv',
'../data\\fhv_tripdata_2015-03.csv',
'../data\\fhv_tripdata_2015-04.csv',
'../data\\fhv_tripdata_2015-05.csv']

 

3. 그런 다음 각각의 파일을 데이터프레임으로 저장합니다.

taxi1 = pd.read_csv(nyc_taxi_data[0]) 
taxi2 = pd.read_csv(nyc_taxi_data[1]) 
taxi3 = pd.read_csv(nyc_taxi_data[2]) 
taxi4 = pd.read_csv(nyc_taxi_data[3]) 
taxi5 = pd.read_csv(nyc_taxi_data[4])

 

4. 데이터를 잘 볼러왔는지 확인합니다.

print(taxi1.head(n=2)) 
print(taxi2.head(n=2)) 
print(taxi3.head(n=2)) 
print(taxi4.head(n=2)) 
print(taxi5.head(n=2))


      Dispatching_base_num                 Pickup_date    locationID
0                             B00013    2015-01-01 00:30:00            NaN
1                             B00013    2015-01-01 01:22:00            NaN
      Dispatching_base_num                 Pickup_date    locationID
0                             B00013    2015-02-01 00:00:00            NaN
1                             B00013    2015-02-01 00:01:00            NaN
      Dispatching_base_num                 Pickup_date    locationID
0                             B00029    2015-03-01 00:02:00            NaN
1                             B00029    2015-03-01 00:03:00            NaN
      Dispatching_base_num                 Pickup_date    locationID
0                             B00001    2015-04-01 04:30:00            NaN
1                             B00001    2015-04-01 06:00:00            NaN
      Dispatching_base_num                 Pickup_date    locationID
0                             B00001    2015-05-01 04:30:00            NaN
1                             B00001    2015-05-01 05:00:00            NaN

 

5. 각 데이터의 구조, 즉 행과 열을 확인해 보겠습니다. 데이터가 꽤 크다는 것을 알 수 있습니다.

print(taxi1.shape) 
print(taxi2.shape) 
print(taxi3.shape) 
print(taxi4.shape) 
print(taxi5.shape)

(2746033, 3)
(3126401, 3)
(3281427, 3)
(3917789, 3)
(4296067, 3)

 

6. 이제 데이터 처리를 위해 각 데이터프레임을 연결해야 합니다. 다음은 concat 메서드로 모든 데이터프레임(taxi1~5)을 연결한 것입니다.

taxi = pd.concat([taxi1, taxi2, taxi3, taxi4, taxi5])

print(taxi.shape)

(17367717, 3)

 

 

* 반복문으로 데이터 준비하기

반복문을 응용하면 단 몇 줄의 코드로 데이터를 준비할 수 있습니다. 앞의 실습에서 생성한 파일 목록(nyc_taxi_data)을 반복문으로 읽어 들인 다음 리스트(list_taxi_df)에 이어붙이면 됩니다. 그러면 리스트에 데이터프레임이 순서대로 저장됩니다.

list_taxi_df = [] 

for csv_filename in nyc_taxi_data:
    # print(csv_filename)
    df = pd.read_csv(csv_filename)
    list_taxi_df.append(df) 

print(len(list_taxi_df))

5

print(type(list_taxi_df[0]))

<class 'pandas.core.frame.DataFrame'>

print(list_taxi_df[0].head( ))

      Dispatching_base_num                 Pickup_date    locationID
0                             B00013    2015-01-01 00:30:00            NaN
1                             B00013    2015-01-01 01:22:00            NaN
2                             B00013    2015-01-01 01:23:00            NaN
3                             B00013    2015-01-01 01:44:00            NaN
4                             B00013    2015-01-01 02:00:00            NaN

taxi_loop_concat = pd.concat(list_taxi_df) 
print(taxi_loop_concat.shape)

(17367717, 3)

print(taxi.equals(taxi_loop_concat))

True

 

 

 

 

마무리하며

이 장에서는 깔끔한 데이터를 만드는 방법에 대해 알아보았습니다. 넓은 데이터를 분석하기 좋은 형태로 만들기 위한 피벗부터 중복 데이터를 처리하는 방법과 여러 열을 하나로 합치는 실습까지 진행해 보았습니다. 데이터 분석에서 깔끔한 데이터는 매우 중요합니다. 반드시 07장에서 공부한 내용을 완벽하게 이해하고 다음 장으로 넘어가세요.

 

 

 

 

 

출처 : "판다스 입문"