অভিধানের ভিত্তিতে ডেটাফ্রেমে নতুন কলাম যুক্ত করুন


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')কাজটি সম্পন্ন হয়েছে তবে অকারণে অনেক পান্ডাস বস্তু তৈরির সাথে কারও পক্ষে এই দীর্ঘ সমাধানটিকে পছন্দ করা উচিত নয়।
piRSquared

আবার, ক্ষমাপ্রার্থী, আপনার আমার উত্সাহও রয়েছে, আমি কেবল উত্তরগুলি সহজ উত্তরগুলির জন্য গাইড করতে চাই।
piRSquared

@ পিআইআরস্কয়ারে আমি দুপুরের খাবারের জন্য গিয়েছিলাম, এবং আমি ফিরে আসার পরে এটি যে মনোযোগ পেয়েছিল তা অবাক করে দিয়েছিলাম। আমি একমত যে এটি একটি সাধারণ mergeকিছু সম্পাদন করতে পারে এমন কিছু করতে কিছুটা বিশৃঙ্খলাবদ্ধ । আমি অনুভব করেছি যে উত্তরটি দ্রুত পোস্ট হবে তাই আমি একটি বিকল্প বেছে নিয়েছি এবং কোনও কারণে আমার মনে মাল্টিআইডিস রয়েছে। আমি সম্মত, এটি সম্ভবত গ্রহণযোগ্য উত্তর হওয়া উচিত নয়, তাই আশা করি এটি ঘটবে না।
ALOLZ

1
ওহ আমি তোমার সাথে আছি আমি অনেকবার একই উত্তর দিয়েছি। আমি কেবল সম্প্রদায়ের সেবা করার জন্য যথাসাধ্য চেষ্টা করছি (-: আমি বিশ্বাস করি আপনি আমার উদ্দেশ্যটি পেয়েছেন।
piRSareared

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))... প্রক্রিয়াটি পুনরায় বলার জন্য, প্রক্রিয়া করার সময় সমস্ত কিছু পছন্দসই ধরণের থাকে।
পিয়ারস্কোয়ার্ড

1
df.assign(score=[score.get(t, 0) for t in zip(*map(df.get, df))])
পিয়ারস্কোয়ার্ড

1
সবশেষে, আমি যা লিখছি তার বেশিরভাগটি ব্লাস্টার কারণ হ্যাশ হ্যাশ হ্যাশের 1.0সমান কারণ 1তাই টিপল লুকগুলি একই উত্তর হিসাবে নির্বিশেষে হওয়া উচিত। এই এত মন্তব্যের জন্য দুঃক্ষিত @Alexander কিন্তু আমি শুধু মানুষ এই আরো ভোট দিন করতে চান, কারণ ... তারা উচিত (: -
piRSquared

1
যতক্ষণ আপনি সময় নিচ্ছেন ততক্ষণ আমার পরামর্শটি দেখুন। এমন অনুষ্ঠানগুলি যখন .valuesব্যয়বহুল হয়
পিআইআরস্কোয়ার্ড

1
@AndyL। আপনি কোন কলামগুলি এবং কোন ক্রমে এটি নিয়ন্ত্রণ করতে পারেন: zip(*map(df.get, ['col2', 'col1', 'col5']))বা এর পরিবর্তনের টিপলস পেতে পারেন df:zip(*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
আপনি কী চান তার একটি উদাহরণ যুক্ত করুন
দানি মেসেজো

সাধারণ উদাহরণ: # এখানে 40 এবং 50, 10 এবং 20 হ'ল বয়সসীমা যার জন্য আমার স্কোর = 4 (বা 5) স্কোর = {(1, 40, 50, 1, 1) ব্যবহার করা উচিত: 4, (0, 10, 20) , 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ভাল। তবে, আমার মতে আপনার একটি বোধগম্যতা পছন্দ করা উচিত। দেখুন @ আলেকজান্ডার এর সময়।
পাইরেসওয়ার্ড

ঠিক আছে @ পাইস্কয়ার্ড। সেটা মাথায় রাখবে।
বিষ্ণুদেব
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.