Pandalarda bir değeri başka bir değere göre değiştirme


109

Stata kodumu hız iyileştirmeleri için Python'a yeniden programlamaya çalışıyorum ve PANDAS yönünde işaret edildim. Bununla birlikte, verileri nasıl işleyeceğime kafamı sarmakta zorlanıyorum.

Diyelim ki 'ID' sütun başlığındaki tüm değerleri yinelemek istiyorum. Bu kimlik belirli bir numarayla eşleşirse, karşılık gelen iki değeri FirstName ve LastName'i değiştirmek istiyorum.

Stata'da şöyle görünür:

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

Yani bu, FirstName'deki ID == 103'ün Matt'e karşılık gelen tüm değerlerinin yerini alır.

PANDAS'ta böyle bir şey deniyorum

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

Buradan nereye gideceğinden emin değilim. Herhangi bir fikir?

Yanıtlar:


184

Bir seçenek, koşulunuzun tuttuğu yerleri mantıksal olarak değerlendirmek ve oradaki verilerin üzerine yazmak için Python'un dilimleme ve indeksleme özelliklerini kullanmaktır.

Doğrudan verilerinizi yükleyebilirsiniz varsayarsak pandasile pandas.read_csvsonra aşağıdaki kodu sizin için yararlı olabilir.

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

Yorumlarda belirtildiği gibi, atamayı her iki sütuna da tek seferde yapabilirsiniz:

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

pandasÜzerine locyazma atama işlemlerinden yararlanmak için 0.11 veya daha yeni bir sürüme ihtiyacınız olacağını unutmayın .


Bunu yapmanın başka bir yolu da zincirleme atama denen şeyi kullanmaktır. Bunun davranışı daha az kararlıdır ve bu nedenle en iyi çözüm olarak kabul edilmez ( belgelerde açıkça önerilmemektedir ), ancak aşağıdakiler hakkında bilgi sahibi olmak yararlıdır:

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

16
bu tadı da eklemeye ne dersiniz:df.loc[df.ID == 103, ['FirstName', 'LastName']] = 'Matt', 'Jones'
Boud

2
-1 "Bunu yapmanın başka bir yolu, zincirleme atama denen şeyi kullanmaktır." Hayır. Kesinlikle hayır. Bu var sadece zincirleme atama güvenilir olmadığını bilmek faydalı. Güvenilir, optimal olmayan bir çözüm değil, durum çok daha kötü . Hatta Stack Overflow'da başka bir yerde bunu kabul ettiniz . Lütfen, zincirleme atamanın geçerli bir seçenek olduğu yanılsamasını vermekten kaçının. Verdiğiniz ilk iki yöntem yeterliydi ve bunu yapmanın tercih edilen yolu.
Phillip Bulut

9
Katılmıyorum. Zincirleme görevlendirmenin uygulanabilir bir yol olmadığını ileri sürmeye çalışırken bilgiççe ısrarla neden ısrar ettiğinizi anlamıyorum. Tercih edilen yol olarak görülmediğini kabul ettim. Daha fazla İstediğiniz ne. Bunu yapmanın bir yolu değilmiş gibi davranmak saçma . Aslında, şu anda benim sistemimde (sürüm 0.8), bunu yapmanın doğru yolu bu. Bu pozisyonu alacaksanız, oy vermenizle ilgilenmiyorum. Olumsuz oyla kendi fikrinizi belirtmekten çekinmeyin, ancak ben sizin fikrinizi zaten düşündüm ve buna katılmıyorum.
ely

11
İnternet ciddi bir iştir. Her halükarda, EMS, seçeneğin var olduğunu bildiğimi takdir ettim.
Parseltongue

Karşılaşabileceğiniz bir sorun, csv'nin sütun adlarında nokta / noktalara sahip olması ve atamaların karışmasıdır. Sütunları şu şekilde düzeltebilirsiniz: cols = df.columns cols = cols.map (lambda x: x.replace ('.', '_') İf isinstance (x, str) else x) df.columns = cols
ski_squaw

37

Kullanabilirsiniz, mapbir diktoneriden veya hatta özel bir işlevden valeleri eşleyebilir.

Bunun sizin df'niz olduğunu varsayalım:

    ID First_Name Last_Name
0  103          a         b
1  104          c         d

Diktleri oluşturun:

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

Ve harita:

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

Sonuç şu şekilde olacaktır:

    ID First_Name Last_Name
0  103       Matt     Jones
1  104         Mr         X

Veya özel bir işlev kullanın:

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

2
Bu, diktenizde değerler yoksa bir KeyError oluşturmaz mı?
EdChum

1
Özel işlev olacak, diğerleri yine de çalışacak. Ama dictharitalama için yaratıldığını varsaydım . Aksi takdirde, aşağıdaki gibi bir şeye dayalı olarak bazı kontroller / temizlik yapılabilir:df.ID.isin(names.keys())
Rutger Kassies

Özel işlev herhangi bir (anonim olmayan) işleve genişletilebilir.
user989762

14

Orijinal soru, belirli bir dar kullanım durumuna yöneliktir. Daha genel yanıtlara ihtiyaç duyanlar için işte bazı örnekler:

Diğer sütunlardaki verileri kullanarak yeni bir sütun oluşturma

Aşağıdaki veri çerçevesi göz önüne alındığında:

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

Aşağıda , seriler için geçersiz kılınan işlemi descriptionkullanarak diğer sütunların birleşimi olarak yeni bir sütun ekliyoruz +. Süslü dize biçimlendirmesi, f dizeleri vb. Burada çalışmaz çünkü +skalarlar için geçerlidir ve 'ilkel' değerler için geçerli değildir:

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

Biz almak 1 yearskedi (yerine için 1 yearbiz conditionals kullanarak aşağıdaki sabitleme edilecektir).

Mevcut bir sütunu koşullularla değiştirme

Burada, orijinal animalsütunu diğer sütunlardan değerlerle değiştiriyoruz ve np.whereaşağıdakilerin değerine dayalı bir koşullu alt dize ayarlamak için kullanıyoruz 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

Koşullu birden çok sütunu değiştirme

Daha esnek bir yaklaşım, .apply()tek bir sütun yerine tüm bir veri çerçevesini çağırmaktır :

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

Yukarıdaki kodda, transform_row(r)işlev Seriesbelirli bir satırı temsil eden bir nesneyi alır (ile gösterilir axis=1, varsayılan değeri her sütun için axis=0bir Seriesnesne sağlayacaktır ). Sütun adlarını kullanarak satırdaki gerçek 'ilkel' değerlere erişebildiğimiz ve verilen satır / sütundaki diğer hücrelerin görünürlüğüne sahip olduğumuz için bu, işlemeyi kolaylaştırır.


1
Bu kadar kapsamlı bir cevap yazmaya zaman ayırdığınız için teşekkürler. Çok minnettarım.
Parseltongue

Bu son derece yararlı cevap için teşekkürler. Bir takip - bir dizeyi değiştirmek yerine sütunda matematik yaparak bir sütunu değiştirmek istersek ne olur? Örneğin, yukarıdaki örneği kullanarak, df.age sütununu df.animal == 'köpek' ise 7 ile çarpmak istersek ne olur? Teşekkür ederim!
GbG

1
@GbG: np.wheremuhtemelen aradığınız şeydir, örneğin stackoverflow.com/a/42540310/191246'ya bakın, ancak mantığı skaler bir işleme sığdıramamanız da mümkündür, o zaman açıkça dönüştürmeniz gerekir Hücre sayısal olarak nasıl yapıldığına benzertransform_row
ccpizza

Teşekkürler @ccpizza! Tam da aradığım şey.
GbG

13

Bu soru, Bay Kassies'in cevabına bir zeyilname sunmaya değecek kadar sık ​​ziyaret edilebilir. dictYerleşik sınıfının alt sınıflandırılır varsayılan bir 'kayıp' tuşları için döndürülür, böylece olabilir. Bu mekanizma pandalar için iyi çalışıyor. Ancak aşağıya bakın.

Bu şekilde önemli hatalardan kaçınmak mümkündür.

>>> 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         

Aynı şey aşağıdaki şekilde daha basit bir şekilde yapılabilir. getBir dikt nesnesinin yöntemi için 'varsayılan' bağımsız değişkeninin kullanılması, bir dikteyi alt sınıflara ayırmayı gereksiz kılar.

>>> 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
Bu, mükemmel varsayılan işlemlerle, gördüğüm en iyi ve en kolay cevap. Teşekkür ederim.
Brendan

@Brendan: Oh! Çok teşekkürler.
Bill Bell
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.