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.