8장: 팬더와 기본 데이터 논쟁

장 구성


학습 목표


  • df.head(), df.tail(), df.info(), df.describe()를 사용하여 데이터프레임을 검사합니다.
  • df.info()df.describe()를 사용하여 데이터프레임 요약을 얻습니다.
  • pd.set_option("display.max_rows", n)과 같은 Pandas 구성 옵션을 수정하여 Jupyter에서 데이터프레임이 표시되는 방식을 조작합니다.
  • df.rename() 함수를 사용하거나 df.columns 속성에 액세스하여 데이터프레임의 열 이름을 바꿉니다.
  • .set_index(), .reset_index() , df.index.name, .index를 사용하여 데이터프레임의 인덱스 이름과 인덱스 값을 수정합니다.
  • df.melt()df.pivot()을 사용하여 데이터프레임의 모양을 변경하고, 특히 깔끔한 데이터프레임을 만드세요.
  • df.merge()pd.concat()을 사용하여 데이터프레임을 결합하고 이러한 다양한 방법을 언제 사용해야 하는지 알아보세요.
  • 데이터 프레임 df.apply()df.applymap()에 함수 적용
  • df.groupby()df.agg()를 사용하여 그룹화 및 집계 작업을 수행합니다.
  • df.agg()를 사용하여 데이터 프레임에서 값의 최소값, 최대값 및 합계를 찾는 등 그룹화되거나 그룹화되지 않은 개체에 대해 집계 방법을 수행합니다.
  • df.dropna()df.fillna()를 사용하여 데이터프레임에서 누락된 값을 제거하거나 채웁니다.

1. 데이터프레임 특성


지난 장에서는 데이터프레임을 생성하는 방법을 살펴보았습니다. 이제 데이터프레임을 볼 수 있는 몇 가지 유용한 방법을 살펴보겠습니다.

import numpy as np
import pandas as pd

머리/꼬리

.head().tail() 메서드를 사용하면 데이터프레임의 상단/하단 n(기본값 5) 행을 볼 수 있습니다. 지난 장의 사이클링 데이터 세트를 로드하여 시험해 보겠습니다.

df = pd.read_csv("data/cycling_data.csv")
df.head()
Date Name Type Time Distance Comments
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week

기본 반환 값은 5개 행이지만 원하는 숫자를 전달할 수 있습니다. 예를 들어 상위 10개 행을 살펴보겠습니다.

df.head(10)
Date Name Type Time Distance Comments
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
5 16 Sep 2019, 13:57:48 Morning Ride Ride 2272 12.45 Rested after the weekend!
6 17 Sep 2019, 00:15:47 Afternoon Ride Ride 1973 12.45 Legs feeling strong!
7 17 Sep 2019, 13:43:34 Morning Ride Ride 2285 12.60 Raining
8 18 Sep 2019, 13:49:53 Morning Ride Ride 2903 14.57 Raining today
9 18 Sep 2019, 00:15:52 Afternoon Ride Ride 2101 12.48 Pumped up tires

또는 아래쪽 5개 행:

df.tail()
Date Name Type Time Distance Comments
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
31 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

데이터프레임 요약

데이터프레임의 상위 수준 요약을 얻는 데 매우 유용한 세 가지 속성/함수는 다음과 같습니다. - .shape -.info() - .describe()

.shape는 이전에 본 ndarray 속성과 같습니다. 데이터프레임의 모양(행, 열)을 제공합니다.

df.shape
(33, 6)

.info()는 dtype, 메모리 사용량, null이 아닌 값 등과 같은 데이터프레임 자체에 대한 정보를 인쇄합니다.

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 33 entries, 0 to 32
Data columns (total 6 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   Date      33 non-null     object 
 1   Name      33 non-null     object 
 2   Type      33 non-null     object 
 3   Time      33 non-null     int64  
 4   Distance  31 non-null     float64
 5   Comments  33 non-null     object 
dtypes: float64(1), int64(1), object(4)
memory usage: 1.7+ KB

.describe()는 데이터프레임 내 값의 요약 통계를 제공합니다.

df.describe()
Time Distance
count 33.000000 31.000000
mean 3512.787879 12.667419
std 8003.309233 0.428618
min 1712.000000 11.790000
25% 1863.000000 12.480000
50% 2118.000000 12.620000
75% 2285.000000 12.750000
max 48062.000000 14.570000

기본적으로 .describe()는 숫자 기능의 요약만 인쇄합니다. include='all' 인수를 사용하여 모든 기능에 대한 요약을 제공하도록 강제할 수 있습니다(비록 말이 안 될 수도 있지만!):

df.describe(include="all")
Date Name Type Time Distance Comments
count 33 33 33 33.000000 31.000000 33
unique 33 2 1 NaN NaN 25
top 20 Sep 2019, 01:02:05 Afternoon Ride Ride NaN NaN Rested after the weekend!
freq 1 17 33 NaN NaN 3
mean NaN NaN NaN 3512.787879 12.667419 NaN
std NaN NaN NaN 8003.309233 0.428618 NaN
min NaN NaN NaN 1712.000000 11.790000 NaN
25% NaN NaN NaN 1863.000000 12.480000 NaN
50% NaN NaN NaN 2118.000000 12.620000 NaN
75% NaN NaN NaN 2285.000000 12.750000 NaN
max NaN NaN NaN 48062.000000 14.570000 NaN

데이터프레임 표시하기

데이터프레임을 효과적으로 표시하는 것은 작업 흐름에서 중요한 부분이 될 수 있습니다. 데이터프레임에 60개 이상의 행이 있는 경우 Pandas는 처음 5개와 마지막 5개 행만 표시합니다.

pd.DataFrame(np.random.rand(100))
0
0 0.643224
1 0.617756
2 0.650490
3 0.289595
4 0.469394
... ...
95 0.549403
96 0.187836
97 0.016904
98 0.733392
99 0.875343

100 rows × 1 columns

행이 60개 미만인 데이터프레임의 경우 Pandas는 전체 데이터프레임을 인쇄합니다.

df
Date Name Type Time Distance Comments
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
5 16 Sep 2019, 13:57:48 Morning Ride Ride 2272 12.45 Rested after the weekend!
6 17 Sep 2019, 00:15:47 Afternoon Ride Ride 1973 12.45 Legs feeling strong!
7 17 Sep 2019, 13:43:34 Morning Ride Ride 2285 12.60 Raining
8 18 Sep 2019, 13:49:53 Morning Ride Ride 2903 14.57 Raining today
9 18 Sep 2019, 00:15:52 Afternoon Ride Ride 2101 12.48 Pumped up tires
10 19 Sep 2019, 00:30:01 Afternoon Ride Ride 48062 12.48 Feeling good
11 19 Sep 2019, 13:52:09 Morning Ride Ride 2090 12.59 Getting colder which is nice
12 20 Sep 2019, 01:02:05 Afternoon Ride Ride 2961 12.81 Feeling good
13 23 Sep 2019, 13:50:41 Morning Ride Ride 2462 12.68 Rested after the weekend!
14 24 Sep 2019, 00:35:42 Afternoon Ride Ride 2076 12.47 Oiled chain, bike feels smooth
15 24 Sep 2019, 13:41:24 Morning Ride Ride 2321 12.68 Bike feeling much smoother
16 25 Sep 2019, 00:07:21 Afternoon Ride Ride 1775 12.10 Feeling really tired
17 25 Sep 2019, 13:35:41 Morning Ride Ride 2124 12.65 Stopped for photo of sunrise
18 26 Sep 2019, 00:13:33 Afternoon Ride Ride 1860 12.52 raining
19 26 Sep 2019, 13:42:43 Morning Ride Ride 2350 12.91 Detour around trucks at Jericho
20 27 Sep 2019, 01:00:18 Afternoon Ride Ride 1712 12.47 Tired by the end of the week
21 30 Sep 2019, 13:53:52 Morning Ride Ride 2118 12.71 Rested after the weekend!
22 1 Oct 2019, 00:15:07 Afternoon Ride Ride 1732 NaN Legs feeling strong!
23 1 Oct 2019, 13:45:55 Morning Ride Ride 2222 12.82 Beautiful morning! Feeling fit
24 2 Oct 2019, 00:13:09 Afternoon Ride Ride 1756 NaN A little tired today but good weather
25 2 Oct 2019, 13:46:06 Morning Ride Ride 2134 13.06 Bit tired today but good weather
26 3 Oct 2019, 00:45:22 Afternoon Ride Ride 1724 12.52 Feeling good
27 3 Oct 2019, 13:47:36 Morning Ride Ride 2182 12.68 Wet road
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
31 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

저는 60개 행 임계값이 약간 과하다고 생각합니다. 저는 20개 정도를 더 선호합니다. pd.set_option("display.max_rows", 20)을 사용하여 설정을 변경하면 20개 이상의 행이 있는 항목은 이전처럼 첫 번째와 마지막 5개 행으로 요약됩니다.

pd.set_option("display.max_rows", 20)
df
Date Name Type Time Distance Comments
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
... ... ... ... ... ... ...
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
31 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

33 rows × 6 columns

표시되는 열 수, 숫자 형식 지정 방법 등 변경할 수 있는 다른 표시 옵션도 있습니다. 자세한 내용은 공식 문서를 참조하세요.

제가 지적할 표시 옵션 중 하나는 Pandas를 사용하면 음수 값을 강조 표시하거나 데이터 프레임에 조건부 색상 맵을 추가하는 등 테이블 스타일을 지정할 수 있다는 것입니다. 아래에서는 음수(보라색)에서 양수(노란색)까지의 값을 기준으로 값의 스타일을 지정하겠습니다. 더 많은 예를 보려면 스타일링 문서를 참조하세요.

test = pd.DataFrame(
    np.random.randn(5, 5),
    index=[f"row_{_}" for _ in range(5)],
    columns=[f"feature_{_}" for _ in range(5)],
)
test.style.background_gradient(cmap="plasma")
feature_0 feature_1 feature_2 feature_3 feature_4
row_0 -0.519284 -0.553179 -1.768810 1.023342 -0.668625
row_1 -1.064582 0.205538 0.350555 -1.942761 1.594635
row_2 1.057886 0.074919 -0.998969 -0.030725 0.677628
row_3 0.898228 -0.697138 -0.134935 -1.592920 -2.127225
row_4 0.039582 0.372877 -0.299539 -2.132972 2.194797

조회수와 매수

이전 장에서 우리는 뷰(기존 객체의 일부를 “보는 것”)와 복사본(메모리에 객체의 새로운 복사본을 만드는 것)에 대해 논의했습니다. Pandas에서는 이러한 것들이 약간 추상화되어 “…뷰 또는 복사본을 반환할지 예측하기가 매우 어렵습니다”(이는 Pandas 문서의 전용 섹션에서 인용문입니다).

기본적으로 수행하려는 작업, 데이터 프레임 구조 및 기본 배열의 메모리 레이아웃에 따라 다릅니다. 하지만 걱정하지 마세요. 여러분이 알아야 할 모든 것을 알려드리겠습니다. 첫째, Pandas에서 접하게 되는 가장 일반적인 경고는 ’SettingWithCopy’입니다. Pandas는 자신이 하고 있다고 생각하는 작업을 수행하지 않을 수도 있다는 경고로 이를 발생시킵니다. 예를 살펴보겠습니다. 우리 데이터프레임에 하나의 이상치 ’Time’이 있다는 것을 기억하실 것입니다.

df[df["Time"] > 4000]
Date Name Type Time Distance Comments
10 19 Sep 2019, 00:30:01 Afternoon Ride Ride 48062 12.48 Feeling good

이것을 ’2000’으로 변경하고 싶다고 상상해 보세요. 아마도 다음과 같이 할 것입니다:

df[df["Time"] > 4000]["Time"] = 2000
/opt/miniconda3/lib/python3.7/site-packages/ipykernel_launcher.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  """Entry point for launching an IPython kernel.

곱셈이나 제곱과 같은 계열에 대한 표준 연산을 수행할 수도 있습니다. NumPy는 또한 시리즈가 numpy 배열로 구축되었기 때문에 대부분의 함수에 대한 인수로 시리즈를 허용합니다(나중에 자세히 설명).

df[df["Time"] > 4000]
Date Name Type Time Distance Comments
10 19 Sep 2019, 00:30:01 Afternoon Ride Ride 48062 12.48 Feeling good

아니, 그렇지 않았습니다. 아마도 당신은 그렇다고 생각했지만요. 위에서 일어난 일은 df[df['Time'] > 4000]이 먼저 실행되어 데이터프레임의 복사본을 반환했다는 것입니다. id()를 사용하여 확인할 수 있습니다.

print(f"The id of the original dataframe is: {id(df)}")
print(f" The id of the indexed dataframe is: {id(df[df['Time'] > 4000])}")
The id of the original dataframe is: 5762156560
 The id of the indexed dataframe is: 5781171152

그런 다음 ['Time'] = 2000을 추가하여 이 새 객체에 값을 설정하려고 했습니다. Pandas는 원본 데이터프레임의 복사본에서 해당 작업을 수행하고 있다고 경고하는데 이는 아마도 우리가 원하는 것이 아닐 것입니다. 이 문제를 해결하려면 .loc[]를 사용하여 한 번에 색인을 생성해야 합니다. 예를 들면 다음과 같습니다.

df.loc[df["Time"] > 4000, "Time"] = 2000

이번에는 오류가 없습니다! 그리고 변경 사항을 확인해 보겠습니다.

df[df["Time"] > 4000]
Date Name Type Time Distance Comments

두 번째로 알아야 할 것은 무엇인가가 뷰인지 복사본인지 확실하지 않은 경우 .copy() 메서드를 사용하여 데이터프레임의 복사본을 강제로 만들 수 있다는 것입니다. 다음과 같습니다:

df2 = df[df["Time"] > 4000].copy()

이렇게 하면 원하는 대로 수정할 수 있는 복사본이 보장됩니다.

2. 기본 DataFrame 조작


열 이름 바꾸기

두 가지 방법으로 열 이름을 바꿀 수 있습니다. 1. .rename() 사용(선택적으로 열 이름 변경) 2. .columns 속성을 설정하여 (모든 열 이름을 한 번에 변경하려면)

df
Date Name Type Time Distance Comments
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
... ... ... ... ... ... ...
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
31 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

33 rows × 6 columns

한번 시도해 봅시다:

df.rename(columns={"Date": "Datetime", "Comments": "Notes"})
df
Date Name Type Time Distance Comments
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
... ... ... ... ... ... ...
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
31 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

33 rows × 6 columns

기다리다? 무슨 일이에요? 아무것도 바뀌지 않았나요? 위의 코드에서는 실제로 데이터프레임의 열 이름을 바꾸었지만 데이터프레임을 수정하지 않고 복사본을 만들었습니다. 영구적인 데이터프레임 변경에는 일반적으로 두 가지 옵션이 있습니다. - 1. 대부분의 함수/메소드에서 사용할 수 있는 inplace=True 인수(예: df.rename(..., inplace=True))를 사용합니다. - 2. 재할당(예: df = df.rename(...)) Pandas 팀에서는 몇 가지 이유로 인해 방법 2(재할당)을 권장합니다(대부분 내부적으로 메모리가 할당되는 방식과 관련이 있음).

df = df.rename(columns={"Date": "Datetime", "Comments": "Notes"})
df
Datetime Name Type Time Distance Notes
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
... ... ... ... ... ... ...
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
31 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

33 rows × 6 columns

데이터프레임의 모든 열을 변경하려면 .columns 속성을 설정하면 됩니다.

df.columns = [f"Column {_}" for _ in range(1, 7)]
df
Column 1 Column 2 Column 3 Column 4 Column 5 Column 6
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
... ... ... ... ... ... ...
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
31 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

33 rows × 6 columns

색인 변경

세 가지 주요 방법으로 데이터프레임의 인덱스 레이블을 변경할 수 있습니다. 1. 데이터 프레임의 열 중 하나를 인덱스로 만드는 .set_index() 2. df.index.name을 직접 수정하여 인덱스 이름 변경 3. .reset_index()는 현재 인덱스를 열로 이동하고 0부터 시작하는 정수 레이블로 인덱스를 재설정합니다. 4. .index() 속성을 직접 수정합니다.

df
Column 1 Column 2 Column 3 Column 4 Column 5 Column 6
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
... ... ... ... ... ... ...
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
31 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

33 rows × 6 columns

아래에서는 인덱스를 Column 1로 설정하고 인덱스 이름을 “New Index”로 바꿉니다.

df = df.set_index("Column 1")
df.index.name = "New Index"
df
Column 2 Column 3 Column 4 Column 5 Column 6
New Index
10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
... ... ... ... ... ...
4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

33 rows × 5 columns

.reset_index()를 사용하여 인덱스를 다시 열로 보내고 기본 정수 인덱스를 가질 수 있습니다.

df = df.reset_index()
df
New Index Column 2 Column 3 Column 4 Column 5 Column 6
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
... ... ... ... ... ... ...
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
31 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

33 rows × 6 columns

열 이름과 마찬가지로 인덱스를 직접 수정할 수도 있지만 이렇게 한 기억은 없습니다. 일반적으로 .set_index()를 사용합니다.

df.index
RangeIndex(start=0, stop=33, step=1)
df.index = range(100, 133, 1)
df
New Index Column 2 Column 3 Column 4 Column 5 Column 6
100 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
101 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
102 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
103 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
104 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
... ... ... ... ... ... ...
128 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
129 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
130 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
131 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
132 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

33 rows × 6 columns

열 추가/제거

데이터프레임의 열을 추가/제거하는 두 가지 주요 방법이 있습니다. 1. []를 사용하여 열을 추가하세요. 2. .drop()을 사용하여 열을 삭제합니다.

사이클링 데이터세트의 새로운 사본을 다시 읽어보겠습니다.

df = pd.read_csv("data/cycling_data.csv")
df
Date Name Type Time Distance Comments
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
... ... ... ... ... ... ...
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
31 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

33 rows × 6 columns

새 열 이름과 값과 함께 []를 사용하면 데이터프레임에 새 열을 추가할 수 있습니다.

df["Rider"] = "Tom Beuzen"
df["Avg Speed"] = df["Distance"] * 1000 / df["Time"]  # avg. speed in m/s
df
Date Name Type Time Distance Comments Rider Avg Speed
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain Tom Beuzen 6.055662
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain Tom Beuzen 5.148163
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather Tom Beuzen 6.720344
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise Tom Beuzen 5.857664
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week Tom Beuzen 6.599683
... ... ... ... ... ... ... ... ...
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind Tom Beuzen 6.754011
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good Tom Beuzen 5.909725
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break! Tom Beuzen 6.838675
31 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise Tom Beuzen 5.192854
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump Tom Beuzen 6.397179

33 rows × 8 columns

df = df.drop(columns=["Rider", "Avg Speed"])
df
Date Name Type Time Distance Comments
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
... ... ... ... ... ... ...
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
31 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

33 rows × 6 columns

행 추가/제거

데이터프레임에 행을 수동으로 추가하는 경우는 많지 않습니다(일반적으로 연결/조인을 통해 행을 추가합니다. 이에 대해서는 다음에 설명하겠습니다). 다음 두 가지 방법으로 데이터프레임의 행을 추가/제거할 수 있습니다. 1. .append()를 사용하여 행을 추가합니다. 2. .drop()을 사용하여 행을 삭제합니다.

df
Date Name Type Time Distance Comments
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
... ... ... ... ... ... ...
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
31 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

33 rows × 6 columns

이 데이터프레임의 맨 아래에 새 행을 추가해 보겠습니다.

another_row = pd.DataFrame(
    [
        [
            "12 Oct 2019, 00:10:57",
            "Morning Ride",
            "Ride",
            2331,
            12.67,
            "Washed and oiled bike last night",
        ]
    ],
    columns=df.columns,
    index=[33],
)
df = df.append(another_row)
df
Date Name Type Time Distance Comments
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
... ... ... ... ... ... ...
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
31 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump
33 12 Oct 2019, 00:10:57 Morning Ride Ride 2331 12.67 Washed and oiled bike last night

34 rows × 6 columns

.drop()을 사용하여 인덱스 30 위의 모든 행을 삭제할 수 있습니다.

df.drop(index=range(30, 34))
Date Name Type Time Distance Comments
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
... ... ... ... ... ... ...
25 2 Oct 2019, 13:46:06 Morning Ride Ride 2134 13.06 Bit tired today but good weather
26 3 Oct 2019, 00:45:22 Afternoon Ride Ride 1724 12.52 Feeling good
27 3 Oct 2019, 13:47:36 Morning Ride Ride 2182 12.68 Wet road
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good

30 rows × 6 columns

3. 데이터프레임 재구성


Tidy 데이터는 “데이터세트의 구조를 의미(의미)와 연결”에 관한 것입니다. 이는 다음과 같이 정의됩니다. 1. 각 변수는 열을 형성합니다. 2. 각 관측치가 행을 형성합니다. 3. 각 유형의 관측 단위가 테이블을 구성합니다.

데이터프레임을 깔끔하게 만들기 위해(또는 다른 목적으로) 데이터프레임의 모양을 변경해야 하는 경우가 많습니다.

출처: r4ds

용융 및 피벗

Pandas .melt(), .pivot().pivot_table()은 데이터프레임 재구성에 도움이 될 수 있습니다. - .melt(): 넓은 데이터를 길게 만듭니다. - .pivot(): 데이터 폭을 길게 만듭니다. - .pivot_table(): .pivot()과 동일하지만 여러 인덱스를 처리할 수 있습니다.

출처: Garrick Aden-Buie의 GitHub

아래 데이터는 다양한 강사가 다양한 연도에 걸쳐 가르친 강좌 수를 보여줍니다. 당신이 대답하고 싶은 질문이 “연도에 따라 가르치는 과목의 수가 달라지나요?”와 같은 것이라면, 다음은 한 행에 1년 동안 진행된 과정에 대한 관찰이 여러 개 있기 때문에 아마도 정리된 것으로 간주되지 않을 것입니다(즉, 2018, 2019 및 2020에 대한 데이터가 단일 행에 있음).

df = pd.DataFrame(
    {
        "Name": ["Tom", "Mike", "Tiffany", "Varada", "Joel"],
        "2018": [1, 3, 4, 5, 3],
        "2019": [2, 4, 3, 2, 1],
        "2020": [5, 2, 4, 4, 3],
    }
)
df
Name 2018 2019 2020
0 Tom 1 2 5
1 Mike 3 4 2
2 Tiffany 4 3 4
3 Varada 5 2 4
4 Joel 3 1 3

.melt()로 깔끔하게 만들어 보겠습니다. .melt()는 몇 가지 인수를 취하는데, 가장 중요한 것은 “식별자”가 되어야 하는 열을 나타내는 id_vars입니다.

df_melt = df.melt(id_vars="Name", var_name="Year", value_name="Courses")
df_melt
Name Year Courses
0 Tom 2018 1
1 Mike 2018 3
2 Tiffany 2018 4
3 Varada 2018 5
4 Joel 2018 3
5 Tom 2019 2
6 Mike 2019 4
7 Tiffany 2019 3
8 Varada 2019 2
9 Joel 2019 1
10 Tom 2020 5
11 Mike 2020 2
12 Tiffany 2020 4
13 Varada 2020 4
14 Joel 2020 3

value_vars 인수를 사용하면 “용해”하려는 특정 변수를 선택할 수 있습니다(value_vars를 지정하지 않으면 식별자가 아닌 모든 열이 사용됩니다). 예를 들어 아래에서는 2018 열을 생략했습니다.

df.melt(
    id_vars="Name", value_vars=["2019", "2020"], var_name="Year", value_name="Courses"
)
Name Year Courses
0 Tom 2019 2
1 Mike 2019 4
2 Tiffany 2019 3
3 Varada 2019 2
4 Joel 2019 1
5 Tom 2020 5
6 Mike 2020 2
7 Tiffany 2020 4
8 Varada 2020 4
9 Joel 2020 3

때로는 .pivot()을 사용하여 긴 데이터를 넓게 만들고 싶을 때가 있습니다. .pivot()을 사용할 때 피벗할 인덱스와 더 넓은 데이터프레임의 새 열을 만드는 데 사용될 을 지정해야 합니다.

df_pivot = df_melt.pivot(index="Name", columns="Year", values="Courses")
df_pivot
Year 2018 2019 2020
Name
Joel 3 1 3
Mike 3 4 2
Tiffany 4 3 4
Tom 1 2 5
Varada 5 2 4

Pandas는 지정된 index를 새 데이터 프레임의 인덱스로 설정하고 열의 레이블을 유지한다는 것을 알 수 있습니다. 이러한 이름을 쉽게 제거하고 인덱스를 재설정하여 데이터프레임을 원래처럼 보이게 만들 수 있습니다.

df_pivot = df_pivot.reset_index()
df_pivot.columns.name = None
df_pivot
Name 2018 2019 2020
0 Joel 3 1 3
1 Mike 3 4 2
2 Tiffany 4 3 4
3 Tom 1 2 5
4 Varada 5 2 4

.pivot()은 종종 원하는 것을 얻을 수 있지만 다음과 같은 경우에는 작동하지 않습니다. - 여러 인덱스를 사용하거나(다음 장) - 중복된 인덱스/열 레이블이 있습니다.

이러한 경우 .pivot_table()을 사용해야 합니다. 여기서는 pivot()에 대해 먼저 배우는 것이 좋기 때문에 여기서는 너무 많이 집중하지 않겠습니다.

df = pd.DataFrame(
    {
        "Name": ["Tom", "Tom", "Mike", "Mike"],
        "Department": ["CS", "STATS", "CS", "STATS"],
        "2018": [1, 2, 3, 1],
        "2019": [2, 3, 4, 2],
        "2020": [5, 1, 2, 2],
    }
).melt(id_vars=["Name", "Department"], var_name="Year", value_name="Courses")
df
Name Department Year Courses
0 Tom CS 2018 1
1 Tom STATS 2018 2
2 Mike CS 2018 3
3 Mike STATS 2018 1
4 Tom CS 2019 2
5 Tom STATS 2019 3
6 Mike CS 2019 4
7 Mike STATS 2019 2
8 Tom CS 2020 5
9 Tom STATS 2020 1
10 Mike CS 2020 2
11 Mike STATS 2020 2

위의 경우 이름에 중복이 있으므로 pivot()이 작동하지 않습니다. ValueError: Index에 중복 항목이 포함되어 있어 모양을 변경할 수 없습니다가 발생합니다.

df.pivot(index="Name", columns="Year", values="Courses")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-41-585e29950052> in <module>
      1 df.pivot(index="Name",
      2          columns="Year",
----> 3          values="Courses")

/opt/miniconda3/lib/python3.7/site-packages/pandas/core/frame.py in pivot(self, index, columns, values)
   6669         from pandas.core.reshape.pivot import pivot
   6670 
-> 6671         return pivot(self, index=index, columns=columns, values=values)
   6672 
   6673     _shared_docs[

/opt/miniconda3/lib/python3.7/site-packages/pandas/core/reshape/pivot.py in pivot(data, index, columns, values)
    475         else:
    476             indexed = data._constructor_sliced(data[values]._values, index=index)
--> 477     return indexed.unstack(columns)
    478 
    479 

/opt/miniconda3/lib/python3.7/site-packages/pandas/core/series.py in unstack(self, level, fill_value)
   3888         from pandas.core.reshape.reshape import unstack
   3889 
-> 3890         return unstack(self, level, fill_value)
   3891 
   3892     # ----------------------------------------------------------------------

/opt/miniconda3/lib/python3.7/site-packages/pandas/core/reshape/reshape.py in unstack(obj, level, fill_value)
    423             return _unstack_extension_series(obj, level, fill_value)
    424         unstacker = _Unstacker(
--> 425             obj.index, level=level, constructor=obj._constructor_expanddim,
    426         )
    427         return unstacker.get_result(

/opt/miniconda3/lib/python3.7/site-packages/pandas/core/reshape/reshape.py in __init__(self, index, level, constructor)
    118             raise ValueError("Unstacked DataFrame is too big, causing int32 overflow")
    119 
--> 120         self._make_selectors()
    121 
    122     @cache_readonly

/opt/miniconda3/lib/python3.7/site-packages/pandas/core/reshape/reshape.py in _make_selectors(self)
    167 
    168         if mask.sum() < len(self.index):
--> 169             raise ValueError("Index contains duplicate entries, cannot reshape")
    170 
    171         self.group_index = comp_index

ValueError: Index contains duplicate entries, cannot reshape

그러한 경우 .pivot_table()을 사용합니다. 중복 항목에 집계 함수를 적용합니다. 이 경우에는 ’sum()’을 사용합니다.

df.pivot_table(index="Name", columns="Year", values="Courses", aggfunc="sum")
Year 2018 2019 2020
Name
Mike 4 6 4
Tom 3 5 6

부서별 번호를 유지하려면 NameDepartment를 모두 여러 인덱스로 지정할 수 있습니다.

df.pivot_table(index=["Name", "Department"], columns="Year", values="Courses")
Year 2018 2019 2020
Name Department
Mike CS 3 4 2
STATS 1 2 2
Tom CS 1 2 5
STATS 2 3 1

위의 결과는 다중 인덱스 또는 “계층적으로 인덱스된” 데이터 프레임입니다(다음 장에서 자세히 설명). 사용해야 하는 경우 문서에서 pivot_table()에 대해 자세히 알아볼 수 있습니다.

4. 여러 DataFrame으로 작업하기


함께 묶거나 병합하려는 여러 데이터프레임을 사용하여 작업하는 경우가 많습니다. df.merge()df.concat()은 데이터프레임 결합에 대해 알아야 할 전부입니다. Pandas 문서는 이러한 기능에 매우 유용하지만 이해하기가 매우 쉽습니다.

Note

이 섹션에 표시된 조인 예는 Jenny Bryan의 STAT 545 자료의 15장에서 영감을 받았습니다.

pd.concat()을 사용하여 데이터프레임 고정

pd.concat()을 사용하여 데이터프레임을 서로 붙일 수 있습니다: - 수직: 동일한 이 있는 경우 또는 - 가로: 동일한 이 있는 경우

df1 = pd.DataFrame({"A": [1, 3, 5], "B": [2, 4, 6]})
df2 = pd.DataFrame({"A": [7, 9, 11], "B": [8, 10, 12]})
df1
A B
0 1 2
1 3 4
2 5 6
df2
A B
0 7 8
1 9 10
2 11 12
pd.concat((df1, df2), axis=0)  # axis=0 specifies a vertical stick, i.e., on the columns
A B
0 1 2
1 3 4
2 5 6
0 7 8
1 9 10
2 11 12

인덱스가 단순히 서로 결합되었다는 점에 주목하세요. 이것은 당신이 원하는 것일 수도 있고 아닐 수도 있습니다. 인덱스를 재설정하려면 ‘ignore_index=True’ 인수를 지정하면 됩니다.

pd.concat((df1, df2), axis=0, ignore_index=True)
A B
0 1 2
1 3 4
2 5 6
3 7 8
4 9 10
5 11 12

가로로 서로 붙이려면 axis=1을 사용하세요.

pd.concat((df1, df2), axis=1, ignore_index=True)
0 1 2 3
0 1 2 7 8
1 3 4 9 10
2 5 6 11 12

두 개의 데이터프레임으로 제한되지 않고 원하는 만큼 연결할 수 있습니다.

pd.concat((df1, df2, df1, df2), axis=0, ignore_index=True)
A B
0 1 2
1 3 4
2 5 6
3 7 8
4 9 10
5 11 12
6 1 2
7 3 4
8 5 6
9 7 8
10 9 10
11 11 12

pd.merge()를 사용하여 DataFrame 결합하기

pd.merge()는 다른 규칙을 사용하여 데이터프레임을 “결합”하는 기능을 제공합니다(SQL에 익숙하다면 SQL과 마찬가지로). df.merge()를 사용하여 공유 열을 기반으로 데이터프레임을 조인할 수 있습니다. 방법은 다음과 같습니다. - “내부 조인” - “외부 조인” - “왼쪽 조인” - “오른쪽 가입”

자세한 내용은 이 훌륭한 치트 시트이 훌륭한 애니메이션을 참조하세요.

df1 = pd.DataFrame(
    {
        "name": [
            "Magneto",
            "Storm",
            "Mystique",
            "Batman",
            "Joker",
            "Catwoman",
            "Hellboy",
        ],
        "alignment": ["bad", "good", "bad", "good", "bad", "bad", "good"],
        "gender": ["male", "female", "female", "male", "male", "female", "male"],
        "publisher": [
            "Marvel",
            "Marvel",
            "Marvel",
            "DC",
            "DC",
            "DC",
            "Dark Horse Comics",
        ],
    }
)
df2 = pd.DataFrame(
    {"publisher": ["DC", "Marvel", "Image"], "year_founded": [1934, 1939, 1992]}
)

‘내부’ 조인은 df2에서 ’publisher’에 대한 일치 값이 발견된 df1의 모든 행을 반환합니다.

pd.merge(df1, df2, how="inner", on="publisher")
name alignment gender publisher year_founded
0 Magneto bad male Marvel 1939
1 Storm good female Marvel 1939
2 Mystique bad female Marvel 1939
3 Batman good male DC 1934
4 Joker bad male DC 1934
5 Catwoman bad female DC 1934

“외부” 조인은 df1df2의 모든 행을 반환하여 정보를 사용할 수 없는 곳에 NaN을 배치합니다.

pd.merge(df1, df2, how="outer", on="publisher")
name alignment gender publisher year_founded
0 Magneto bad male Marvel 1939.0
1 Storm good female Marvel 1939.0
2 Mystique bad female Marvel 1939.0
3 Batman good male DC 1934.0
4 Joker bad male DC 1934.0
5 Catwoman bad female DC 1934.0
6 Hellboy good male Dark Horse Comics NaN
7 NaN NaN NaN Image 1992.0

df1의 모든 행과 df1df2의 모든 열을 반환하며, 일치하는 부분이 채워집니다.

pd.merge(df1, df2, how="left", on="publisher")
name alignment gender publisher year_founded
0 Magneto bad male Marvel 1939.0
1 Storm good female Marvel 1939.0
2 Mystique bad female Marvel 1939.0
3 Batman good male DC 1934.0
4 Joker bad male DC 1934.0
5 Catwoman bad female DC 1934.0
6 Hellboy good male Dark Horse Comics NaN

pd.merge(df1, df2, how="right", on="publisher")
name alignment gender publisher year_founded
0 Batman good male DC 1934
1 Joker bad male DC 1934
2 Catwoman bad female DC 1934
3 Magneto bad male Marvel 1939
4 Storm good female Marvel 1939
5 Mystique bad female Marvel 1939
6 NaN NaN NaN Image 1992

데이터프레임을 조인하기 위해 ‘키’를 지정하는 방법은 여러 가지가 있습니다. 인덱스 값, 다른 항목, 열 이름 등에 대해 조인할 수 있습니다. 또 다른 유용한 인수는 데이터프레임에서 일치 항목이 발견된 위치를 알려주는 결과에 열을 추가하는 ’indicator’ 인수입니다.

pd.merge(df1, df2, how="outer", on="publisher", indicator=True)
name alignment gender publisher year_founded _merge
0 Magneto bad male Marvel 1939.0 both
1 Storm good female Marvel 1939.0 both
2 Mystique bad female Marvel 1939.0 both
3 Batman good male DC 1934.0 both
4 Joker bad male DC 1934.0 both
5 Catwoman bad female DC 1934.0 both
6 Hellboy good male Dark Horse Comics NaN left_only
7 NaN NaN NaN Image 1992.0 right_only

그런데 pd.concat()을 사용하면 여러 데이터드램에서 간단한 “내부” 또는 “외부” 조인을 동시에 수행할 수 있습니다. 병합보다 유연성은 떨어지지만 때로는 유용할 수 있습니다.

5. 추가 DataFrame 작업


사용자 정의 기능 적용

Pandas에 내장되지 않은 기능을 적용하고 싶을 때가 있을 것입니다. 이를 위해 다음과 같은 방법도 있습니다. - df.apply(), 데이터프레임 전체에 걸쳐 열 방향 또는 행 방향으로 함수를 적용합니다(함수는 배열을 허용/반환할 수 있어야 합니다). - df.applymap(), 요소별로 함수를 적용합니다(한 번에 단일 값을 허용/반환하는 함수의 경우). - series.apply()/series.map(), 위와 동일하지만 Pandas 시리즈용

예를 들어 데이터프레임의 열에 numpy 함수를 사용한다고 가정해 보겠습니다.

df = pd.read_csv("data/cycling_data.csv")
df[["Time", "Distance"]].apply(np.sin)
Time Distance
0 -0.901866 0.053604
1 -0.901697 0.447197
2 -0.035549 -0.046354
3 -0.739059 0.270228
4 -0.236515 -0.086263
... ... ...
28 -0.683372 0.063586
29 0.150056 0.133232
30 0.026702 0.023627
31 -0.008640 0.221770
32 0.897861 -0.700695

33 rows × 2 columns

또는 사용자 정의 기능을 적용할 수도 있습니다.

def seconds_to_hours(x):
    return x / 3600


df[["Time"]].apply(seconds_to_hours)
Time
0 0.578889
1 0.703056
2 0.517500
3 0.608889
4 0.525278
... ...
28 0.519444
29 0.596944
30 0.511389
31 0.684167
32 0.511944

33 rows × 1 columns

이것은 람다 함수로 더 좋았을 수도 있습니다 …

df[["Time"]].apply(lambda x: x / 3600)
Time
0 0.578889
1 0.703056
2 0.517500
3 0.608889
4 0.525278
... ...
28 0.519444
29 0.596944
30 0.511389
31 0.684167
32 0.511944

33 rows × 1 columns

추가 인수가 필요한 함수를 사용할 수도 있습니다. .apply()에 인수를 지정하기만 하면 됩니다.

def convert_seconds(x, to="hours"):
    if to == "hours":
        return x / 3600
    elif to == "minutes":
        return x / 60


df[["Time"]].apply(convert_seconds, to="minutes")
Time
0 34.733333
1 42.183333
2 31.050000
3 36.533333
4 31.516667
... ...
28 31.166667
29 35.816667
30 30.683333
31 41.050000
32 30.716667

33 rows × 1 columns

일부 함수는 스칼라만 허용/반환합니다.

int(3.141)
3
float([3.141, 10.345])
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-62-97f9c30c3ff1> in <module>
----> 1 float([3.141, 10.345])

TypeError: float() argument must be a string or a number, not 'list'

이를 위해서는 .applymap()이 필요합니다:

df[["Time"]].applymap(int)
Time
0 2084
1 2531
2 1863
3 2192
4 1891
... ...
28 1870
29 2149
30 1841
31 2463
32 1843

33 rows × 1 columns

그러나 이와 같은 일반적인 기능의 “벡터화된” 버전이 이미 사용 가능하여 훨씬 더 빠른 경우가 많습니다. 위의 경우 .astype()을 사용하여 전체 열의 dtype을 빠르게 변경할 수 있습니다.

time_applymap = %timeit  -q -o -r 3 df[['Time']].applymap(float)
time_builtin = %timeit  -q -o -r 3 df[['Time']].astype(float)
print(
    f"'astype' is {time_applymap.average / time_builtin.average:.2f} faster than 'applymap'!"
)
'astype' is 1.98 faster than 'applymap'!

그룹화

종종 우리는 데이터의 특정 그룹을 조사하는 데 관심이 있습니다. df.groupby()를 사용하면 변수를 기준으로 데이터를 그룹화할 수 있습니다.

df = pd.read_csv("data/cycling_data.csv")
df
Date Name Type Time Distance Comments
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
... ... ... ... ... ... ...
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
31 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

33 rows × 6 columns

이 데이터프레임을 ‘이름’ 열에 그룹화해 보겠습니다.

dfg = df.groupby(by="Name")
dfg
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x1577bb150>

DataFrameGroupBy 객체란 무엇입니까? 여기에는 데이터프레임 그룹에 대한 정보가 포함됩니다.

groupby 객체는 실제로 인덱스 매핑 사전이며, 원하는 경우 다음을 볼 수 있습니다.

dfg.groups
{'Afternoon Ride': [0, 2, 4, 6, 9, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32], 'Morning Ride': [1, 3, 5, 7, 8, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31]}

.get_group() 메소드를 사용하여 그룹에 접근할 수도 있습니다:

dfg.get_group("Afternoon Ride")
Date Name Type Time Distance Comments
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
6 17 Sep 2019, 00:15:47 Afternoon Ride Ride 1973 12.45 Legs feeling strong!
9 18 Sep 2019, 00:15:52 Afternoon Ride Ride 2101 12.48 Pumped up tires
10 19 Sep 2019, 00:30:01 Afternoon Ride Ride 48062 12.48 Feeling good
12 20 Sep 2019, 01:02:05 Afternoon Ride Ride 2961 12.81 Feeling good
14 24 Sep 2019, 00:35:42 Afternoon Ride Ride 2076 12.47 Oiled chain, bike feels smooth
16 25 Sep 2019, 00:07:21 Afternoon Ride Ride 1775 12.10 Feeling really tired
18 26 Sep 2019, 00:13:33 Afternoon Ride Ride 1860 12.52 raining
20 27 Sep 2019, 01:00:18 Afternoon Ride Ride 1712 12.47 Tired by the end of the week
22 1 Oct 2019, 00:15:07 Afternoon Ride Ride 1732 NaN Legs feeling strong!
24 2 Oct 2019, 00:13:09 Afternoon Ride Ride 1756 NaN A little tired today but good weather
26 3 Oct 2019, 00:45:22 Afternoon Ride Ride 1724 12.52 Feeling good
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

그러나 일반적으로 해야 할 일은 groupby 객체에 집계 함수를 적용하는 것입니다.

dfg.mean()
Time Distance
Name
Afternoon Ride 4654.352941 12.462
Morning Ride 2299.875000 12.860

.aggregate()를 사용하여 여러 함수를 적용할 수 있습니다.

dfg.aggregate(["mean", "sum", "count"])
Time Distance
mean sum count mean sum count
Name
Afternoon Ride 4654.352941 79124 17 12.462 186.93 15
Morning Ride 2299.875000 36798 16 12.860 205.76 16

그리고 다른 열에 다른 기능을 적용할 수도 있습니다.

def num_range(x):
    return x.max() - x.min()


dfg.aggregate({"Time": ["max", "min", "mean", num_range], "Distance": ["sum"]})
Time Distance
max min mean num_range sum
Name
Afternoon Ride 48062 1712 4654.352941 46350 186.93
Morning Ride 2903 2090 2299.875000 813 205.76

그런데 그룹화되지 않은 데이터 프레임에도 집계를 사용할 수 있습니다. 이것은 df.describe가 내부적으로 수행하는 작업과 거의 같습니다:

df.agg(["mean", "min", "count", num_range])
Date Name Type Time Distance Comments
min 1 Oct 2019, 00:15:07 Afternoon Ride Ride 1712.000000 11.790000 A little tired today but good weather
count 33 33 33 33.000000 31.000000 33
mean NaN NaN NaN 3512.787879 12.667419 NaN
num_range NaN NaN NaN 46350.000000 2.780000 NaN

누락된 값 처리

누락된 값은 일반적으로 ’NaN’으로 표시됩니다. df.isnull()을 사용하여 데이터프레임에서 누락된 값을 찾을 수 있습니다. 데이터프레임의 각 요소에 대해 부울을 반환합니다.

df.isnull()
Date Name Type Time Distance Comments
0 False False False False False False
1 False False False False False False
2 False False False False False False
3 False False False False False False
4 False False False False False False
... ... ... ... ... ... ...
28 False False False False False False
29 False False False False False False
30 False False False False False False
31 False False False False False False
32 False False False False False False

33 rows × 6 columns

하지만 일반적으로 .any() 또는 .info() 메서드를 사용하여 행이나 열별로 이 정보를 얻는 것이 더 도움이 됩니다.

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 33 entries, 0 to 32
Data columns (total 6 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   Date      33 non-null     object 
 1   Name      33 non-null     object 
 2   Type      33 non-null     object 
 3   Time      33 non-null     int64  
 4   Distance  31 non-null     float64
 5   Comments  33 non-null     object 
dtypes: float64(1), int64(1), object(4)
memory usage: 1.7+ KB
df[df.isnull().any(axis=1)]
Date Name Type Time Distance Comments
22 1 Oct 2019, 00:15:07 Afternoon Ride Ride 1732 NaN Legs feeling strong!
24 2 Oct 2019, 00:13:09 Afternoon Ride Ride 1756 NaN A little tired today but good weather

누락된 값이 있는 경우 일반적으로 해당 값을 삭제하거나 대치합니다.df.dropna()를 사용하여 누락된 값을 삭제할 수 있습니다.

df.dropna()
Date Name Type Time Distance Comments
0 10 Sep 2019, 00:13:04 Afternoon Ride Ride 2084 12.62 Rain
1 10 Sep 2019, 13:52:18 Morning Ride Ride 2531 13.03 rain
2 11 Sep 2019, 00:23:50 Afternoon Ride Ride 1863 12.52 Wet road but nice weather
3 11 Sep 2019, 14:06:19 Morning Ride Ride 2192 12.84 Stopped for photo of sunrise
4 12 Sep 2019, 00:28:05 Afternoon Ride Ride 1891 12.48 Tired by the end of the week
... ... ... ... ... ... ...
28 4 Oct 2019, 01:08:08 Afternoon Ride Ride 1870 12.63 Very tired, riding into the wind
29 9 Oct 2019, 13:55:40 Morning Ride Ride 2149 12.70 Really cold! But feeling good
30 10 Oct 2019, 00:10:31 Afternoon Ride Ride 1841 12.59 Feeling good after a holiday break!
31 10 Oct 2019, 13:47:14 Morning Ride Ride 2463 12.79 Stopped for photo of sunrise
32 11 Oct 2019, 00:16:57 Afternoon Ride Ride 1843 11.79 Bike feeling tight, needs an oil and pump

31 rows × 6 columns

또는 .fillna()를 사용하여 이를 대치(“채우기”)할 수 있습니다. 이 방법에는 채우기를 위한 다양한 옵션이 있으며 고정 값, 열의 평균, 이전의 nan이 아닌 값 등을 사용할 수 있습니다.

df = pd.DataFrame(
    [
        [np.nan, 2, np.nan, 0],
        [3, 4, np.nan, 1],
        [np.nan, np.nan, np.nan, 5],
        [np.nan, 3, np.nan, 4],
    ],
    columns=list("ABCD"),
)
df
A B C D
0 NaN 2.0 NaN 0
1 3.0 4.0 NaN 1
2 NaN NaN NaN 5
3 NaN 3.0 NaN 4
df.fillna(0)  # fill with 0
A B C D
0 0.0 2.0 0.0 0
1 3.0 4.0 0.0 1
2 0.0 0.0 0.0 5
3 0.0 3.0 0.0 4
df.fillna(df.mean())  # fill with the mean
A B C D
0 3.0 2.0 NaN 0
1 3.0 4.0 NaN 1
2 3.0 3.0 NaN 5
3 3.0 3.0 NaN 4
df.fillna(method="bfill")  # backward (upwards) fill from non-nan values
A B C D
0 3.0 2.0 NaN 0
1 3.0 4.0 NaN 1
2 NaN 3.0 NaN 5
3 NaN 3.0 NaN 4
df.fillna(method="ffill")  # forward (downward) fill from non-nan values
A B C D
0 NaN 2.0 NaN 0
1 3.0 4.0 NaN 1
2 3.0 4.0 NaN 5
3 3.0 3.0 NaN 4

마지막으로 때로는 시각화를 사용하여 누락된 값을 식별(패턴)하는 데 도움을 줍니다. 제가 자주 하는 일 중 하나는 누락된 값이 어디에 있는지 파악하기 위해 데이터프레임의 히트맵을 인쇄하는 것입니다. 이 코드를 실행하려면 seaborn을 설치해야 할 수도 있습니다.

``쉬 conda 설치 seaborn ````

import seaborn as sns

sns.set(rc={"figure.figsize": (7, 7)})
df
A B C D
0 NaN 2.0 NaN 0
1 3.0 4.0 NaN 1
2 NaN NaN NaN 5
3 NaN 3.0 NaN 4
sns.heatmap(df.isnull(), cmap="viridis", cbar=False);

# Generate a larger synthetic dataset for demonstration
np.random.seed(2020)
npx = np.zeros((100, 20))
mask = np.random.choice([True, False], npx.shape, p=[0.1, 0.9])
npx[mask] = np.nan
sns.heatmap(pd.DataFrame(npx).isnull(), cmap="viridis", cbar=False);