पांडा में एक और मूल्य के आधार पर एक मूल्य बदलें


107

मैं गति सुधार के लिए अपने स्टैटा कोड को पायथन में फिर से शुरू करने की कोशिश कर रहा हूं, और मुझे पांडा की दिशा में बताया गया। हालाँकि, डेटा को कैसे संसाधित किया जाए, इसके बारे में अपने सिर को लपेटने में मुझे मुश्किल समय आ रहा है।

मान लें कि मैं कॉलम हेड 'ID' में सभी मानों पर पुनरावृति करना चाहता हूं। यदि वह आईडी एक विशिष्ट संख्या से मेल खाती है, तो मैं FirstName और LastName दो संगत मानों को बदलना चाहता हूं।

स्टाटा में ऐसा दिखता है:

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

तो यह FirstName में सभी मानों को प्रतिस्थापित करता है जो ID == 103 के मैट से मेल खाते हैं।

पांडा में, मैं कुछ इस तरह की कोशिश कर रहा हूँ

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

कुछ निश्चित नहीं कि यहाँ से कहाँ जाना है। कोई विचार?

जवाबों:


180

एक विकल्प यह है कि पायथन की स्लाइसिंग और इंडेक्सिंग सुविधाओं का उपयोग उन स्थानों का तार्किक रूप से मूल्यांकन करने के लिए करें जहां आपकी स्थिति रहती है और वहां डेटा को अधिलेखित कर दिया जाता है।

मान लें कि आप में सीधे अपने डेटा लोड कर सकते हैं 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के locलिए उपयोग करने के लिए संस्करण 0.11 या नए की आवश्यकता होगी ।


इसे करने का दूसरा तरीका यह है कि जंजीर असाइनमेंट को क्या कहा जाए। इसका व्यवहार कम स्थिर है और इसलिए इसे सबसे अच्छा समाधान नहीं माना जाता है (यह डॉक्स में स्पष्ट रूप से हतोत्साहित किया गया है), लेकिन इसके बारे में जानना उपयोगी है:

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 "ऐसा करने का एक और तरीका यह है कि जंजीर असाइनमेंट कहा जाता है। नहीं। जोरदार, नहीं। यह केवल पता चला है कि श्रृंखलित काम विश्वसनीय नहीं है उपयोगी। ऐसा नहीं है कि यह एक विश्वसनीय, गैर-इष्टतम समाधान है, स्थिति बहुत खराब है । आपने स्टैक ओवरफ्लो पर भी इसे कहीं और स्वीकार किया है । कृपया यह भ्रम देने से बचने की कोशिश करें कि जंजीर असाइनमेंट एक व्यवहार्य विकल्प है। आपके द्वारा दिए गए पहले दो तरीके पर्याप्त थे, और ऐसा करने का पसंदीदा तरीका है।
फिलिप क्लाउड

9
मैं असहमत हूं। मुझे समझ में नहीं आता है कि क्यों आप लगातार इस बात की कोशिश कर रहे हैं कि जंजीर असाइनमेंट व्यवहार्य तरीका नहीं है। मैंने स्वीकार किया कि इसे पसंदीदा तरीका नहीं माना जाता है। आपको और क्या चाहिए। यह इस तरह से कार्य करने के लिए पूर्वनिर्मित है, ऐसा करने का तरीका नहीं है। वास्तव में, मेरे सिस्टम में अभी (संस्करण 0.8), यह करने का सही तरीका है। यदि आप यह पद लेने जा रहे हैं तो मुझे आपके अप-वोटों में कोई दिलचस्पी नहीं है। बेझिझक अपनी बात को बेझिझक बताएं, लेकिन मैं पहले ही आपकी बात पर गौर कर चुका हूं और इससे असहमत हूं।
ely

11
इंटरनेट एक गंभीर सरोकार है। किसी भी दर पर, ईएमएस, मुझे पता है कि विकल्प मौजूद है की सराहना की।
पैरसैलटॉन्ग

एक समस्या जिसे आप चला सकते हैं, वह यह है कि csv में कॉलम नामों में अवधि / बिंदु हैं और असाइनमेंट गड़बड़ हो जाते हैं। आप कुछ इस तरह का उपयोग करके कॉलम को ठीक कर सकते हैं: cols = df.columns cols = cols.map (lambda x: x.replace ('।,' _ ') यदि आइंस्टीनेंस (x, str) बाकी x: df.columns = है। cols
स्की_स्क्वाव

37

आप उपयोग कर सकते हैं map, यह एक तानाशाह या यहां तक ​​कि एक कस्टम फ़ंक्शन से घाटी को मैप कर सकता है।

मान लीजिए कि यह आपका 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
क्या यह मान उत्पन्न नहीं करेगा यदि मान आपके हुक्म में मौजूद नहीं हैं?
एडुकम

1
कस्टम फ़ंक्शन करेगा, अन्य वैसे भी काम करेंगे। लेकिन मैंने मान लिया कि dictयह मानचित्रण के लिए बनाया गया है। अन्यथा कुछ जाँच / सफाई कुछ इस तरह से की जा सकती है जैसे:df.ID.isin(names.keys())
रटगर्स कासीस

कस्टम फ़ंक्शन को किसी भी (गैर अनाम) फ़ंक्शन में विस्तारित किया जा सकता है।
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उस +ऑपरेशन का उपयोग करके अन्य स्तंभों के एक संयोजन के रूप में एक नया कॉलम जोड़ रहे हैं जो श्रृंखला के लिए ओवरराइड है। फैंसी स्ट्रिंग फॉर्मेटिंग, एफ-स्ट्रिंग्स आदि +स्केलर पर लागू होने के बाद से यहां काम नहीं करेंगे और 'आदिम' मान नहीं:

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
इतना व्यापक उत्तर लिखने के लिए समय निकालने के लिए धन्यवाद। बहुत सराहना की।
परलसटॉन्ग

इस अत्यंत उपयोगी उत्तर के लिए धन्यवाद। एक अनुवर्ती - क्या होगा यदि हम एक स्ट्रिंग को संशोधित करने के बजाय कॉलम पर गणित करके एक कॉलम को संशोधित करना चाहते हैं? उदाहरण के लिए, ऊपर दिए गए उदाहरण का उपयोग करके, क्या होगा यदि हम df.age कॉलम को 7 से गुणा करना चाहते हैं यदि df.animal == 'dog'? धन्यवाद!
GbG

1
@GbG: np.whereसंभवत : आप जिस चीज़ की तलाश कर रहे हैं, वह देखें उदा। stackoverflow.com/a/42540310/191246 लेकिन यह भी संभव है कि आप तर्क को एक अदिश ऑपरेशन में फिट नहीं कर पाएंगे, तो आपको स्पष्ट रूप से रूपांतरित करना होगा सेल संख्यात्मक रूप से समान है कि यह कैसे किया जाता हैtransform_row
ccpizza

शुक्रिया @ccpizza! मुझे इसकी ही खोज थी।
GbG

13

यह प्रश्न अभी भी अक्सर देखा जा सकता है कि यह श्री कासिज़ के उत्तर के लिए एक परिशिष्ट देने के लायक है। 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         

एक ही चीज़ को और अधिक सरल तरीके से किया जा सकता है। getकिसी वस्तु की विधि के लिए 'डिफॉल्ट' तर्क का उपयोग एक तानाशाह को उपवर्गित करने के लिए अनावश्यक बनाता है।

>>> 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
यह अब तक का सबसे अच्छा और आसान उत्तर है, जिसे मैंने उत्कृष्ट डिफ़ॉल्ट हैंडलिंग के साथ देखा है। धन्यवाद।
ब्रेंडन

@ बेंडन: ओह! बहुत बहुत धन्यवाद।
बिल बेल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.