Pandas의 다른 값을 기반으로 한 값 변경


107

속도 향상을 위해 Stata 코드를 Python으로 재 프로그래밍하려고하는데 PANDAS의 지시를 받았습니다. 그러나 데이터 처리 방법에 대해 머리를 감는 데 어려움을 겪고 있습니다.

열 머리글 'ID'의 모든 값을 반복하고 싶다고 가정 해 보겠습니다. 해당 ID가 특정 번호와 일치하면 해당하는 두 개의 값인 FirstName과 LastName을 변경하고 싶습니다.

Stata에서는 다음과 같이 보입니다.

replace FirstName = "Matt" if ID==103
replace LastName =  "Jones" if ID==103

따라서 이것은 ID == 103의 값에 해당하는 FirstName의 모든 값을 Matt로 대체합니다.

PANDAS에서는 이런 식으로 시도하고 있습니다

df = read_csv("test.csv")
for i in df['ID']:
    if i ==103:
          ...

여기서 어디로 가야할지 모르겠습니다. 어떤 아이디어?

답변:


181

한 가지 옵션은 Python의 슬라이싱 및 인덱싱 기능을 사용하여 조건이 유지되는 위치를 논리적으로 평가하고 데이터를 덮어 쓰는 것입니다.

직접으로 데이터를로드 할 수 있습니다 가정 pandas을 가진 것은 pandas.read_csv다음 코드는 당신을 위해 도움이 될 수 있습니다.

import pandas
df = pandas.read_csv("test.csv")
df.loc[df.ID == 103, 'FirstName'] = "Matt"
df.loc[df.ID == 103, 'LastName'] = "Jones"

주석에서 언급했듯이 한 번에 두 열에 할당을 수행 할 수도 있습니다.

df.loc[df.ID == 103, ['FirstName', 'LastName']] = 'Matt', 'Jones'

덮어 쓰기 할당 작업에 pandas사용 하려면 버전 0.11 이상 이 필요 loc합니다.


이를 수행하는 또 다른 방법은 연결 할당이라는 것을 사용하는 것입니다. 이 동작은 덜 안정적이므로 최상의 솔루션으로 간주되지 않지만 ( 문서에서 명시 적으로 권장하지 않음 ) 다음에 대해 아는 것이 유용합니다.

import pandas
df = pandas.read_csv("test.csv")
df['FirstName'][df.ID == 103] = "Matt"
df['LastName'][df.ID == 103] = "Jones"

16
방법도 추가이 맛에 대한 :df.loc[df.ID == 103, ['FirstName', 'LastName']] = 'Matt', 'Jones'
Boud

2
-1 "다른 방법은 연결 할당을 사용하는 것입니다." 아뇨. 그건 단지 체인 할당은 신뢰할 수 없습니다 것을 알고 있으면 유용합니다. 신뢰할 수 있고 최적이 아닌 솔루션이 아니라 상황이 훨씬 더 나쁩니다 . Stack Overflow의 다른 곳 에서도 이것을 인정했습니다 . 연결 할당이 실행 가능한 옵션이라는 착각을 피하십시오. 처음 두 가지 방법으로 충분했으며이를 수행하는 데 선호되는 방법입니다.
Phillip Cloud

9
동의하지 않습니다. 나는 당신이 왜 체인 할당이 실행 가능한 방법이 아니라고 주장하려고 계속 노력하는지 이해하지 못합니다. 나는 그것이 선호되는 방법으로 간주되지 않는다는 것을 인정했습니다. 무엇을 더 원하십니까. 그렇게 하는 방법 이 아닌 것처럼 행동하는 것은 터무니없는 일입니다. 사실, 지금 내 시스템 (버전 0.8)에서는 이것이 올바른 방법 입니다. 당신이이 직책을 맡을 거라면 나는 당신의 찬성 투표에 관심이 없습니다. 반대표로 귀하의 요점을 알려주십시오. 그러나 저는 이미 귀하의 요점을 반영했으며 이에 동의하지 않습니다.
ely

11
인터넷은 심각한 사업입니다. 어쨌든, EMS, 옵션이 있다는 것을 알고 감사했습니다.
Parseltongue 2010 년

한 가지 문제는 csv의 열 이름에 마침표 / 점이 있고 할당이 엉망이라는 것입니다. 다음과 같이 열을 수정할 수 있습니다. cols = df.columns cols = cols.map (lambda x : x.replace ( '.', '_') if isinstance (x, str) else x) df.columns = cols
ski_squaw

37

를 사용할 수 있으며 map, dictonairy 또는 사용자 정의 함수의 값을 매핑 할 수 있습니다.

이것이 귀하의 df라고 가정하십시오.

    ID First_Name Last_Name
0  103          a         b
1  104          c         d

사전을 작성하십시오.

fnames = {103: "Matt", 104: "Mr"}
lnames = {103: "Jones", 104: "X"}

그리고지도 :

df['First_Name'] = df['ID'].map(fnames)
df['Last_Name'] = df['ID'].map(lnames)

결과는 다음과 같습니다.

    ID First_Name Last_Name
0  103       Matt     Jones
1  104         Mr         X

또는 사용자 정의 함수를 사용하십시오.

names = {103: ("Matt", "Jones"), 104: ("Mr", "X")}
df['First_Name'] = df['ID'].map(lambda x: names[x][0])

2
dict에 값이 없으면 KeyError가 발생하지 않습니까?
EdChum

1
사용자 정의 기능은 어쨌든 다른 기능은 작동합니다. 그러나 나는 dict매핑을 위해 생성 되었다고 가정했습니다 . 그렇지 않으면 일부 검사 / 청소 같은 것을 기반으로 수행 할 수 있습니다 :df.ID.isin(names.keys())
룻거 Kassies

사용자 정의 함수는 모든 (익명이 아닌) 함수로 확장 할 수 있습니다.
user989762

14

원래 질문은 특정 좁은 사용 사례를 다룹니다. 더 일반적인 답변이 필요한 사람들을 위해 다음과 같은 몇 가지 예가 있습니다.

다른 열의 데이터를 사용하여 새 열 만들기

아래 데이터 프레임이 주어지면 :

import pandas as pd
import numpy as np

df = pd.DataFrame([['dog', 'hound', 5],
                   ['cat', 'ragdoll', 1]],
                  columns=['animal', 'type', 'age'])

In[1]:
Out[1]:
  animal     type  age
----------------------
0    dog    hound    5
1    cat  ragdoll    1

아래에서는 시리즈에 대해 재정 description의 된 +연산을 사용하여 다른 열의 연결로 새 열을 추가합니다 . 멋진 문자열 형식 지정, f- 문자열 등은 +'기본'값이 아닌 스칼라에 적용 되므로 여기서 작동 하지 않습니다.

df['description'] = 'A ' + df.age.astype(str) + ' years old ' \
                    + df.type + ' ' + df.animal

In [2]: df
Out[2]:
  animal     type  age                description
-------------------------------------------------
0    dog    hound    5    A 5 years old hound dog
1    cat  ragdoll    1  A 1 years old ragdoll cat

조건문을 사용하여 아래에서 수정할 1 years고양이 (대신 1 year)를 얻 습니다 .

조건부로 기존 열 수정

여기에서 원래 animal열을 다른 열의 값으로 np.where바꾸고을 사용하여 의 값을 기반으로 조건부 하위 문자열을 설정합니다 age.

# append 's' to 'age' if it's greater than 1
df.animal = df.animal + ", " + df.type + ", " + \
    df.age.astype(str) + " year" + np.where(df.age > 1, 's', '')

In [3]: df
Out[3]:
                 animal     type  age
-------------------------------------
0   dog, hound, 5 years    hound    5
1  cat, ragdoll, 1 year  ragdoll    1

조건부로 여러 열 수정

보다 유연한 접근 방식은 .apply()단일 열이 아닌 전체 데이터 프레임 을 호출 하는 것입니다.

def transform_row(r):
    r.animal = 'wild ' + r.type
    r.type = r.animal + ' creature'
    r.age = "{} year{}".format(r.age, r.age > 1 and 's' or '')
    return r

df.apply(transform_row, axis=1)

In[4]:
Out[4]:
         animal            type      age
----------------------------------------
0    wild hound    dog creature  5 years
1  wild ragdoll    cat creature   1 year

위의 코드에서 transform_row(r)함수는 Series주어진 행을 나타내는 객체를 사용합니다 (로 표시됨 axis=1, 기본값 은 각 열에 axis=0대한 Series객체를 제공함 ). 이렇게하면 열 이름을 사용하여 행의 실제 '기본'값에 액세스 할 수 있고 주어진 행 / 열의 다른 셀에 대한 가시성을 가질 수 있으므로 처리가 단순화됩니다.


1
시간을내어 포괄적 인 답변을 작성해 주셔서 감사합니다. 매우 감사.
Parseltongue

이 매우 유용한 답변에 감사드립니다. 하나의 후속 조치-문자열을 수정하는 대신 열에서 수학을 수행하여 열을 수정하려면 어떻게해야합니까? 예를 들어, 위의 예를 사용하여 df.animal == 'dog'이면 df.age 열에 7을 곱하려면 어떻게해야합니까? 감사합니다!
GbG

1
@GbG : np.where아마도 당신이 찾고있는 것일 것입니다 . 예를 들어 stackoverflow.com/a/42540310/191246을 참조하십시오. 하지만 논리를 스칼라 연산에 맞출 수 없을 수도 있습니다. 그러면 명시 적으로 변환해야합니다. 에서 수행되는 방법과 수치 적으로 유사한 세포transform_row
ccpizza

@ccpizza 감사합니다! 내가 찾던 바로 그것.
GbG

13

이 질문은 Kassies의 대답에 대한 부록을 제공 할 가치가있을만큼 자주 방문 할 수 있습니다. dict내장 클래스 하위 분류의 기본이 '실종'키에 대해 반환되도록 할 수 있습니다. 이 메커니즘은 판다에게 잘 작동합니다. 그러나 아래를 참조하십시오.

이러한 방식으로 주요 오류를 피할 수 있습니다.

>>> import pandas as pd
>>> data = { 'ID': [ 101, 201, 301, 401 ] }
>>> df = pd.DataFrame(data)
>>> class SurnameMap(dict):
...     def __missing__(self, key):
...         return ''
...     
>>> surnamemap = SurnameMap()
>>> surnamemap[101] = 'Mohanty'
>>> surnamemap[301] = 'Drake'
>>> df['Surname'] = df['ID'].apply(lambda x: surnamemap[x])
>>> df
    ID  Surname
0  101  Mohanty
1  201         
2  301    Drake
3  401         

다음과 같은 방법으로 더 간단하게 동일한 작업을 수행 할 수 있습니다. getdict 객체 의 메서드에 'default'인수를 사용하면 dict의 하위 클래스를 만들 필요가 없습니다.

>>> import pandas as pd
>>> data = { 'ID': [ 101, 201, 301, 401 ] }
>>> df = pd.DataFrame(data)
>>> surnamemap = {}
>>> surnamemap[101] = 'Mohanty'
>>> surnamemap[301] = 'Drake'
>>> df['Surname'] = df['ID'].apply(lambda x: surnamemap.get(x, ''))
>>> df
    ID  Surname
0  101  Mohanty
1  201         
2  301    Drake
3  401         

1
이것은 뛰어난 기본 처리 기능과 함께 내가 본 것 중 가장 좋고 가장 쉬운 대답입니다. 감사합니다.
Brendan

@ 브렌든 : 오! 매우 감사합니다.
Bill Bell
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.