Sözlüğe dayanarak veri çerçevesine yeni sütun ekle


23

Bir veri çerçevesi ve sözlük var. Dataframe için yeni bir sütun eklemek ve sözlüğü dayalı değerlerini hesaplamak gerekir.

Makine öğrenimi, bazı tablolara dayalı yeni özellik ekleme:

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)

Ben aşağıdaki çıktı bekliyoruz:

   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

Yanıtlar:


13

Yana score(tuşlar benzersiz şekilde) bir sözlük olduğu Kullanabileceğimiz MultiIndexhizalama

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
Güzel biri MultiIIndex. Alternatif: df['score'] =df.set_index(['gender', 'age', 'cholesterol', 'smoke']).index.map(score).fillna(0).to_numpy().
Quang Hoang

4
@ALollz, affet beni, cevaplarını seviyorum ama böyle bir cevapta çok fazla oy gördüğümde konuşmam gerek. Bu cevap iyi ve zekidir. Ama bu harika değil. Büyük kazanç elde etmek için çok fazla hareketli parça var. Bu süreçte, yeni oluşturduk dfyoluyla set_indexyeni, Seriesyapıcı aracılığıyla. Eğer atadığınızda dizin hizalama yarar elde rağmen df['score']. Son olarak, fillna(0, downcast='infer')işi bitirir, ancak hiç kimse gereksiz yere birçok panda nesnesinin yaratılmasıyla bu uzun çözümü tercih etmemelidir.
piRSquared

Yine, özür dilerim, benim de benim oyum var, ben sadece basit cevaplar için millet rehberlik etmek istiyorum.
piRSquared

@ piRSquared Öğle yemeğine gittim ve geri döndüğümde dikkatini çekti. Basit bir şeyin mergebaşarabileceği bir şey yapmak için biraz kıvrımlı olduğuna katılıyorum . Yanıtın hızlı bir şekilde yayınlanacağını düşündüm, bu yüzden bir alternatif seçtim ve bazı nedenlerden dolayı aklımda MultiIndices vardı. Katılıyorum, bu muhtemelen kabul edilen cevap olmamalı, umarım bu olmaz.
ALollz

1
Ah seninleyim. Aynı şeyi birçok kez cevapladım. Sadece topluma hizmet etmek için elimden geleni yapıyorum (-: Niyetimi
aldığına

7

assignBir liste kavrama ile kullanmak , scoresözlükten bir değer kümesi (her satır) almak, bulunmazsa varsayılan olarak sıfıra.

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

zamanlamalar

Çeşitli yaklaşımlar göz önüne alındığında, bazı zamanlamaları karşılaştırmak ilginç olurdu.

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

Benim favorim biraz. Bununla birlikte, sadece işlem yaparken her şeyin amaçlanan tipte kaldığından emin olmak score.getiçin itertuplesveya zip(*map(df.get, df))... Tekrarlamak için, bu benim tercih ettiğim yaklaşım.
piRSquared

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

1
Son olarak, yazdığım şeylerin çoğu hüzünlüdür, çünkü hash hash ile 1.0aynıdır, bu 1nedenle tuple aramaları ne olursa olsun aynı cevaba neden olmalıdır. Bu konuda birçok yorum için @Alexander özür dilerim ama sadece insanların bunu daha fazla oylamasını istiyorum çünkü ... (-:
piRSquared

1
Zamanladığınız sürece önerime bakın. .valuesPahalı olduğu zamanlar olur
piRSquared

1
@AndyL. hatta hangi sütunları ve hangi sırayla kontrol edebilirsiniz: zip(*map(df.get, ['col2', 'col1', 'col5']))ya da aşağıdaki değişikliklerden örnekler elde edebilirsiniz df:zip(*map(df.eq(1).get, df))
piRSquared

4

Puan bir sözlük olduğu için haritayı kullanabilirsiniz :

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

Çıktı

   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

Alternatif olarak bir liste kavrayışı kullanabilirsiniz:

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

Sorumu genişletmek istiyorum. Gerçekten sütun değeri aralığı sütun temel eklemeniz gerekir. Örneğin, 40 <yaş <50 ise, o zaman skor = 4 vb ... Şimdi sözlük tam bir değer üzerinde eşler. Aynı gerçek ve diğer anahtarlar için ....
Mikola

1
Gerçekten ne istediğine bir örnek ekle
Dani Mesejo

Basit örnek: # Burada 40 ve 50, 10 ve 20 puan kullanmam gereken yaş aralığıdır = 4 (veya 5) puan = {(1, 40, 50, 1, 1): 4, (0, 10, 20 , 1, 3): 5}
Mikola

@Mikola Yani eğer cinsiyet = 1 ve 40 <yaş <50 ve diğerleri ...
Dani Mesejo

1
@Mikola Her bedeni tanımalısınız, ancak bu noktada başka bir soru sorarsanız daha iyi olduğuna inanıyorum.
Dani Mesejo

4

Liste anlama ve harita:

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

Çıktı:

   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

Veya 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

Kullanmanın başka bir yolu olabilir .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

Basit bir satır çözüm, Kullanım getve tuplesatır bazında,

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

Yukarıdaki çözüm, sırayla istenen sütunlardan başka sütun olmadığını varsayar. Değilse, yalnızca sütunları kullanın

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

Kullanımı score.getiyidir. Ancak, bence bir kavrayışı tercih etmelisiniz. Bakınız @ Alexander'ın zamanlamaları.
piRSquared

Tamam @piSquared. Bunu aklınızda tutacak.
Vishnudev
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.