शब्दकोश पर आधारित डेटाफ़्रेम में नया कॉलम जोड़ें


23

मेरे पास एक डेटाफ्रेम और एक शब्दकोश है। मुझे डेटाफ़्रेम में एक नया कॉलम जोड़ने और शब्दकोश के आधार पर इसके मूल्यों की गणना करने की आवश्यकता है।

मशीन लर्निंग, कुछ तालिका के आधार पर नई सुविधा जोड़ना:

score = {(1, 45, 1, 1) : 4, (0, 1, 2, 1) : 5}
df = pd.DataFrame(data = {
    'gender' :      [1,  1,  0, 1,  1,  0,  0,  0,  1,  0],
    'age' :         [13, 45, 1, 45, 15, 16, 16, 16, 15, 15],
    'cholesterol' : [1,  2,  2, 1, 1, 1, 1, 1, 1, 1],
    'smoke' :       [0,  0,  1, 1, 7, 8, 3, 4, 4, 2]},
     dtype = np.int64)

print(df, '\n')
df['score'] = 0
df.score = score[(df.gender, df.age, df.cholesterol, df.smoke)]
print(df)

मैं निम्नलिखित आउटपुट की उम्मीद करता हूं:

   gender  age  cholesterol  smoke    score
0       1   13            1      0      0 
1       1   45            2      0      0
2       0    1            2      1      5
3       1   45            1      1      4
4       1   15            1      7      0
5       0   16            1      8      0
6       0   16            1      3      0
7       0   16            1      4      0
8       1   15            1      4      0
9       0   15            1      2      0

जवाबों:


13

चूंकि scoreएक शब्दकोश है (इसलिए चाबियाँ अद्वितीय हैं) हम MultiIndexसंरेखण का उपयोग कर सकते हैं

df = df.set_index(['gender', 'age', 'cholesterol', 'smoke'])
df['score'] = pd.Series(score)  # Assign values based on the tuple
df = df.fillna(0, downcast='infer').reset_index()  # Back to columns

   gender  age  cholesterol  smoke  score
0       1   13            1      0      0
1       1   45            2      0      0
2       0    1            2      1      5
3       1   45            1      1      4
4       1   15            1      7      0
5       0   16            1      8      0
6       0   16            1      3      0
7       0   16            1      4      0
8       1   15            1      4      0
9       0   15            1      2      0

1
में से एक अच्छा लगा MultiIIndex। वैकल्पिक: df['score'] =df.set_index(['gender', 'age', 'cholesterol', 'smoke']).index.map(score).fillna(0).to_numpy()
क्वांग होआंग

4
@ वालो, मुझे माफ़ कर दो, मुझे तुम्हारे जवाब बहुत पसंद हैं लेकिन मुझे बोलना होगा जब मैं इस तरह एक जवाब पर इतने सारे अपवित्र दिखूंगा। यह उत्तर ठीक है और चतुर है। लेकिन यह महान नहीं है। बिना किसी महान लाभ के बहुत सारे चलते हुए भाग हैं। इस प्रक्रिया में, आप एक नया बना लिया है dfके माध्यम से set_index, एक नए Seriesनिर्माता के माध्यम से। हालांकि जब आप इसे असाइन करते हैं तो आपको इंडेक्स अलाइनमेंट का लाभ मिलता है df['score']। अंत में, fillna(0, downcast='infer')काम हो जाता है , लेकिन किसी को भी अनावश्यक रूप से कई पांडा वस्तुओं के निर्माण के साथ इस लंबे समाधान को पसंद नहीं करना चाहिए।
17

फिर से, क्षमा याचना, आपके पास मेरा उत्थान है, मैं बस लोगों को सरल उत्तर के लिए मार्गदर्शन करना चाहता हूं।
17

@piRSquared मैं दोपहर के भोजन के लिए गया था, और जब मैं वापस आया तो यह ध्यान आकर्षित किया। मैं मानता हूं कि यह सब कुछ करने के लिए थोड़ा सा जटिल है कि एक साधारण mergeपूरा कर सकता है। मुझे लगा कि उत्तर जल्दी से पोस्ट हो जाएगा इसलिए मैंने एक विकल्प चुना और किसी कारण से मेरे दिमाग में मल्टीआईंडिस था। मैं सहमत हूँ, यह संभवतः स्वीकृत उत्तर नहीं होना चाहिए, इसलिए उम्मीद है कि ऐसा नहीं होगा।
एलोलज

1
ओह मैं तुम्हारे साथ हूं। मैंने कई बार वही जवाब दिया है। मैं समुदाय की सेवा करने के लिए सिर्फ अपना सर्वश्रेष्ठ कर रहा हूं (-: मुझे विश्वास है कि आपको मेरा इरादा मिल जाएगा।
piRSquared

7

assignसूची की समझ के साथ उपयोग करना , scoreशब्दकोश से मानों (प्रत्येक पंक्ति) का एक टपल प्राप्त करना , यदि नहीं मिला तो शून्य पर डिफ़ॉल्ट करना।

>>> df.assign(score=[score.get(tuple(row), 0) for row in df.values])
   gender  age  cholesterol  smoke  score
0       1   13            1      0      0
1       1   45            2      0      0
2       0    1            2      1      5
3       1   45            1      1      4
4       1   15            1      7      0
5       0   16            1      8      0
6       0   16            1      3      0
7       0   16            1      4      0
8       1   15            1      4      0
9       0   15            1      2      0

समय

दृष्टिकोण की विविधता को देखते हुए, मैं हालांकि कुछ समय की तुलना करना दिलचस्प होगा।

# Initial dataframe 100k rows (10 rows of identical data replicated 10k times).
df = pd.DataFrame(data = {
    'gender' :      [1,  1,  0, 1,  1,  0,  0,  0,  1,  0] * 10000,
    'age' :         [13, 45, 1, 45, 15, 16, 16, 16, 15, 15] * 10000,
    'cholesterol' : [1,  2,  2, 1, 1, 1, 1, 1, 1, 1] * 10000,
    'smoke' :       [0,  0,  1, 1, 7, 8, 3, 4, 4, 2] * 10000},
     dtype = np.int64)

%timeit -n 10 df.assign(score=[score.get(tuple(v), 0) for v in df.values])
# 223 ms ± 9.28 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit -n 10 
df.assign(score=[score.get(t, 0) for t in zip(*map(df.get, df))])
# 76.8 ms ± 2.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit -n 10
df.assign(score=[score.get(v, 0) for v in df.itertuples(index=False)])
# 113 ms ± 2.58 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit -n 10 df.assign(score=df.apply(lambda x: score.get(tuple(x), 0), axis=1))
# 1.84 s ± 77.3 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit -n 10
(df
 .set_index(['gender', 'age', 'cholesterol', 'smoke'])
 .assign(score=pd.Series(score))
 .fillna(0, downcast='infer')
 .reset_index()
)
# 138 ms ± 11.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit -n 10
s=pd.Series(score)
s.index.names=['gender','age','cholesterol','smoke']
df.merge(s.to_frame('score').reset_index(),how='left').fillna(0).astype(int)
# 24 ms ± 2.27 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit -n 10
df.assign(score=pd.Series(zip(df.gender, df.age, df.cholesterol, df.smoke))
                .map(score)
                .fillna(0)
                .astype(int))
# 191 ms ± 7.54 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit -n 10
df.assign(score=df[['gender', 'age', 'cholesterol', 'smoke']]
                .apply(tuple, axis=1)
                .map(score)
                .fillna(0))
# 1.95 s ± 134 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

थोड़ा सा मेरा पसंदीदा। हालाँकि, यह सुनिश्चित करने के लिए कि सब कुछ इच्छित प्रकार का रहता है जब score.getमैं प्रसंस्करण का उपयोग करूँगा itertuplesया zip(*map(df.get, df))... पुनरावृत्ति के लिए, यह मेरा पसंदीदा तरीका है।
piRSquared

1
df.assign(score=[score.get(t, 0) for t in zip(*map(df.get, df))])
piRSquared

1
अंत में, मैं जो लिख रहा हूं, वह अधिकांश ब्लर है, क्योंकि हैश 1.0उसी के लिए हैश है, 1इसलिए टुपल लुक अप को उसी जवाब के बिना परिणाम देना चाहिए। इस पर कई टिप्पणियों के लिए क्षमा याचना @Alexander लेकिन मैं सिर्फ इतना चाहता हूं कि लोग इसे और
बढ़ाएं

1
जब तक आप समय दे रहे हैं, मेरे सुझाव को देखें। ऐसे अवसर होते हैं जब .valuesमहंगा होता है
piRSquared

1
@AndyL। आप यह भी नियंत्रित कर सकते हैं कि कौन से कॉलम और किस क्रम में है: zip(*map(df.get, ['col2', 'col1', 'col5']))या एक संशोधन के dfzip(*map(df.eq(1).get, df))
टुपल्स

4

आप मानचित्र का उपयोग कर सकते हैं , क्योंकि स्कोर एक शब्दकोश है:

df['score'] = df[['gender', 'age', 'cholesterol', 'smoke']].apply(tuple, axis=1).map(score).fillna(0)
print(df)

उत्पादन

   gender  age  cholesterol  smoke  score
0       1   13            1      0    0.0
1       1   45            2      0    0.0
2       0    1            2      1    5.0
3       1   45            1      1    4.0
4       1   15            1      7    0.0
5       0   16            1      8    0.0
6       0   16            1      3    0.0
7       0   16            1      4    0.0
8       1   15            1      4    0.0
9       0   15            1      2    0.0

एक विकल्प के रूप में आप एक सूची समझ का उपयोग कर सकते हैं:

df['score'] = [score.get(t, 0) for t in zip(df.gender, df.age, df.cholesterol, df.smoke)]
print(df)

मैं अपने प्रश्न को सीमित करना चाहता हूं। वास्तव में मुझे स्तंभ के मान के आधार पर स्तंभ आधार जोड़ने की आवश्यकता है। उदाहरण के लिए, यदि 40 <उम्र <50 तब तो स्कोर = 4 आदि ... अब कुछ सटीक मूल्य पर नक्शे। वही सच है और अन्य कुंजी के लिए ....
मिकोला

1
एक उदाहरण जोड़ें कि आप वास्तव में क्या चाहते हैं
दानी मेज़ो

सरल उदाहरण: # यहां ४० और ५०, १० और २० की आयु सीमा है जिसके लिए मुझे स्कोर = ४ (या ५) स्कोर = {(१, ४०, ५०, १, १): ४, (०, १०, २०) का उपयोग करना चाहिए , 1, 3): 5}
मिकोला

@ मिकोला तो अगर लिंग = 1 और 40 <उम्र <50 और इतने पर ...
दानी मेज़ो

1
@ मिकोला आपको प्रत्येक शरीर को जानना चाहिए, हालांकि इस बिंदु पर मेरा मानना ​​है कि यदि आप एक और प्रश्न पूछें तो बेहतर है।
दानी मेज़ो

4

सूची की समझ और नक्शा:

df['score'] = (pd.Series(zip(df.gender, df.age, df.cholesterol, df.smoke))
               .map(score)
               .fillna(0)
               .astype(int)
              )

आउटपुट:

   gender  age  cholesterol  smoke  score
0       1   13            1      0      0
1       1   45            2      0      0
2       0    1            2      1      5
3       1   45            1      1      4
4       1   15            1      7      0
5       0   16            1      8      0
6       0   16            1      3      0
7       0   16            1      4      0
8       1   15            1      4      0
9       0   15            1      2      0
9       0   15            1      2    0.0

4

reindex

df['socre']=pd.Series(score).reindex(pd.MultiIndex.from_frame(df),fill_value=0).values
df
Out[173]: 
   gender  age  cholesterol  smoke  socre
0       1   13            1      0      0
1       1   45            2      0      0
2       0    1            2      1      5
3       1   45            1      1      4
4       1   15            1      7      0
5       0   16            1      8      0
6       0   16            1      3      0
7       0   16            1      4      0
8       1   15            1      4      0
9       0   15            1      2      0

या merge

s=pd.Series(score)
s.index.names=['gender','age','cholesterol','smoke']
df=df.merge(s.to_frame('score').reset_index(),how='left').fillna(0)
Out[166]: 
   gender  age  cholesterol  smoke  score
0       1   13            1      0    0.0
1       1   45            2      0    0.0
2       0    1            2      1    5.0
3       1   45            1      1    4.0
4       1   15            1      7    0.0
5       0   16            1      8    0.0
6       0   16            1      3    0.0
7       0   16            1      4    0.0
8       1   15            1      4    0.0
9       0   15            1      2    0.0

2

एक और तरीका हो सकता है .loc[]:

m=df.set_index(df.columns.tolist())
m.loc[list(score.keys())].assign(
           score=score.values()).reindex(m.index,fill_value=0).reset_index()

   gender  age  cholesterol  smoke  score
0       1   13            1      0      0
1       1   45            2      0      0
2       0    1            2      1      5
3       1   45            1      1      4
4       1   15            1      7      0
5       0   16            1      8      0
6       0   16            1      3      0
7       0   16            1      4      0
8       1   15            1      4      0
9       0   15            1      2      0

2

सरल एक लाइन समाधान, उपयोग getऔर tupleपंक्ति-वार,

df['score'] = df.apply(lambda x: score.get(tuple(x), 0), axis=1)

ऊपर समाधान मान रहा है कि क्रम में वांछित लोगों के अलावा कोई कॉलम नहीं हैं। यदि नहीं, तो केवल कॉलम का उपयोग करें

cols = ['gender','age','cholesterol','smoke']
df['score'] = df[cols].apply(lambda x: score.get(tuple(x), 0), axis=1)

का उपयोग score.getअच्छा है। हालांकि, आपको मेरी राय में एक समझदारी पसंद करनी चाहिए। @ अलेक्जेंडर की टाइमिंग देखें ।
17

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