एक कॉलम पांडा से एक NxN मैट्रिक्स बनाएं


11

मेरे पास सूची मूल्य वाले प्रत्येक पंक्ति के साथ डेटाफ्रेम है।

id     list_of_value
0      ['a','b','c']
1      ['d','b','c']
2      ['a','b','c']
3      ['a','b','c']

मुझे एक पंक्ति के साथ एक स्कोर की गणना करनी है और अन्य सभी पंक्तियों के खिलाफ है

उदाहरण के लिए:

Step 1: Take value of id 0: ['a','b','c'],
Step 2: find the intersection between id 0 and id 1 , 
        resultant = ['b','c']
Step 3: Score Calculation => resultant.size / id.size

आईडी 0 और आईडी 1,2,3 के बीच चरण 2,3 को दोहराएं, इसी तरह सभी आईडी के लिए।

और एक एन एक्स एन डेटाफ्रेम बनाएं; इसके जैसा:

-  0  1    2  3
0  1  0.6  1  1
1  1  1    1  1 
2  1  1    1  1
3  1  1    1  1

अभी मेरे कोड में केवल एक ही लूप है:

def scoreCalc(x,queryTData):
    #mathematical calculation
    commonTData = np.intersect1d(np.array(x),queryTData)
    return commonTData.size/queryTData.size

ids = list(df['feed_id'])
dfSim = pd.DataFrame()

for indexQFID in range(len(ids)):
    queryTData = np.array(df.loc[df['id'] == ids[indexQFID]]['list_of_value'].values.tolist())

    dfSim[segmentDfFeedIds[indexQFID]] = segmentDf['list_of_value'].apply(scoreCalc,args=(queryTData,))

क्या ऐसा करने के लिए इससे अच्छा तरीका है? क्या मैं सिर्फ एक लूप पुनरावृत्ति करने के बजाय एक लागू फ़ंक्शन लिख सकता हूं। क्या मैं इसे तेज कर सकता हूं?


1
संपादित प्रश्न, @Babydesta
श्रीराम अरविंद

1
यह ६ नहीं है, यह ०. it है, परिणामी है। २ = २, आईडी.इज़ = ३
श्रीराम अरविंद

आपका डेटा कब तक है? और पूरी तरह से कितने मूल्यों में होते हैं list_of_value?
क्वांग होआंग

प्रत्येक list_of_value में अधिकतम 20 मान
श्रीराम अरविंद

प्रत्येक में नहीं list_of_value। मेरा मतलब कुल मिलाकर, सभी पंक्तियों में है।
क्वांग होआंग

जवाबों:


7

यदि आपका डेटा बहुत बड़ा नहीं है, तो आप get_dummiesमानों को एन्कोड करने और मैट्रिक्स गुणा करने के लिए उपयोग कर सकते हैं :

s = pd.get_dummies(df.list_of_value.explode()).sum(level=0)
s.dot(s.T).div(s.sum(1))

आउटपुट:

          0         1         2         3
0  1.000000  0.666667  1.000000  1.000000
1  0.666667  1.000000  0.666667  0.666667
2  1.000000  0.666667  1.000000  1.000000
3  1.000000  0.666667  1.000000  1.000000

अपडेट : यहां कोड के लिए एक संक्षिप्त विवरण दिया गया है। मुख्य विचार दिए गए सूचियों को एक-हॉट-एन्कोडेड में बदलना है:

   a  b  c  d
0  1  1  1  0
1  0  1  1  1
2  1  1  1  0
3  1  1  1  0

एक बार हमारे पास, दो पंक्तियों के प्रतिच्छेदन का आकार, कहते हैं, 0और1 सिर्फ उनका डॉट उत्पाद है, क्योंकि एक चरित्र दोनों पंक्तियों के अंतर्गत आता है यदि और केवल अगर यह प्रतिनिधित्व करता है1 दोनों जाता है।

इसे ध्यान में रखते हुए, पहले उपयोग करें

df.list_of_value.explode()

प्रत्येक सेल को एक श्रृंखला में बदलने के लिए और उन सभी श्रृंखलाओं को समेटना। आउटपुट:

0    a
0    b
0    c
1    d
1    b
1    c
2    a
2    b
2    c
3    a
3    b
3    c
Name: list_of_value, dtype: object

अब, हम उपयोग करते हैं pd.get_dummies उस श्रृंखला का इसे एक हॉट-एन्कोडेड डेटाफ़्रेम में बदलने के लिए करते हैं:

   a  b  c  d
0  1  0  0  0
0  0  1  0  0
0  0  0  1  0
1  0  0  0  1
1  0  1  0  0
1  0  0  1  0
2  1  0  0  0
2  0  1  0  0
2  0  0  1  0
3  1  0  0  0
3  0  1  0  0
3  0  0  1  0

जैसा कि आप देख सकते हैं, प्रत्येक मूल्य की अपनी पंक्ति है। चूंकि हम उन्हें एक ही पंक्ति में एक ही मूल पंक्ति से जोड़ना चाहते हैं, इसलिए हम मूल सूचकांक द्वारा उन्हें योग कर सकते हैं। इस प्रकार

s = pd.get_dummies(df.list_of_value.explode()).sum(level=0)

बाइनरी-एन्कोडेड डेटाफ़्रेम हम चाहते हैं। अगली पंक्ति

s.dot(s.T).div(s.sum(1))

बस अपने तर्क के रूप में है: s.dot(s.T)पंक्तियों द्वारा डॉट उत्पादों की गणना करता है, फिर .div(s.sum(1))पंक्तियों द्वारा गिनती को विभाजित करता है।


12k पंक्तियों के डेटाफ्रेम
श्रीराम अरविंद लक्ष्मणकुमार

@ श्रीराम अरविंदलक्ष्मणकुमार 12k पंक्तियों के साथ, आप डेटाफ़्रेम के साथ समाप्त होंगे 12k x 12k। यदि आपके पास कुछ सौ अद्वितीय मूल्य हैं तो ठीक होना चाहिए।
क्वांग होआंग

कोड भी समझा सकता है?
श्रीराम अरविंद लक्ष्मणकुमार

ज़रूर, लेकिन क्या यह काम करता है?
क्वांग होआंग

1
@ श्रीराम अरविंदलक्ष्मणकुमार मेरा समाधान स्वीकार करने के लिए धन्यवाद। कृपया स्पष्टीकरण और विचार तर्क के लिए अद्यतन देखें।
क्वांग होआंग

3

इसे इस्तेमाल करे

range_of_ids = range(len(ids))

def score_calculation(s_id1,s_id2):
    s1 = set(list(df.loc[df['id'] == ids[s_id1]]['list_of_value'])[0])
    s2 = set(list(df.loc[df['id'] == ids[s_id2]]['list_of_value'])[0])
    # Resultant calculation s1&s2
    return round(len(s1&s2)/len(s1) , 2)


dic = {indexQFID:  [score_calculation(indexQFID,ind) for ind in range_of_ids] for indexQFID in range_of_ids}
dfSim = pd.DataFrame(dic)
print(dfSim)

उत्पादन

     0        1      2       3
0   1.00    0.67    1.00    1.00
1   0.67    1.00    0.67    0.67
2   1.00    0.67    1.00    1.00
3   1.00    0.67    1.00    1.00

आप इसे निम्नानुसार भी कर सकते हैं

dic = {indexQFID:  [round(len(set(s1)&set(s2))/len(s1) , 2) for s2 in df['list_of_value']] for indexQFID,s1 in zip(df['id'],df['list_of_value']) }
dfSim = pd.DataFrame(dic)
print(dfSim)

2

सेट की सूची पर नेस्टेड सूची समझ का उपयोग करें s_list। सूची समझ के भीतर, intersectionअतिव्यापी जाँच के लिए ऑपरेशन का उपयोग करें और प्रत्येक परिणाम की लंबाई प्राप्त करें। अंत में, डेटाफ्रेम का निर्माण करें और इसे प्रत्येक सूची की लंबाई से विभाजित करेंdf.list_of_value

s_list =  df.list_of_value.map(set)
overlap = [[len(s1 & s) for s1 in s_list] for s in s_list]

df_final = pd.DataFrame(overlap) / df.list_of_value.str.len().to_numpy()[:,None]

Out[76]:
          0         1         2         3
0  1.000000  0.666667  1.000000  1.000000
1  0.666667  1.000000  0.666667  0.666667
2  1.000000  0.666667  1.000000  1.000000
3  1.000000  0.666667  1.000000  1.000000

यदि प्रत्येक सूची में डुप्लिकेट मान हैं, तो आपको collections.Counterइसके बजाय उपयोग करना चाहिए set। मैंने नमूना डेटा आईडी = 0 से ['a','a','c']और आईडी = 1 को बदल दिया['d','b','a']

sample df:
id     list_of_value
0      ['a','a','c'] #changed
1      ['d','b','a'] #changed
2      ['a','b','c']
3      ['a','b','c']

from collections import Counter

c_list =  df.list_of_value.map(Counter)
c_overlap = [[sum((c1 & c).values()) for c1 in c_list] for c in c_list]

df_final = pd.DataFrame(c_overlap) / df.list_of_value.str.len().to_numpy()[:,None]


 Out[208]:
          0         1         2         3
0  1.000000  0.333333  0.666667  0.666667
1  0.333333  1.000000  0.666667  0.666667
2  0.666667  0.666667  1.000000  1.000000
3  0.666667  0.666667  1.000000  1.000000

2

अपडेट किया गया

चूंकि बहुत सारे उम्मीदवार समाधान प्रस्तावित हैं, इसलिए समय विश्लेषण करना एक अच्छा विचार है। मैंने ओपी द्वारा अनुरोधित 12k पंक्तियों के साथ कुछ यादृच्छिक डेटा उत्पन्न किया, प्रति सेट 3 तत्वों के साथ रखते हुए लेकिन सेटों को आबाद करने के लिए उपलब्ध वर्णमाला के आकार का विस्तार किया। यह वास्तविक डेटा से मेल खाने के लिए समायोजित किया जा सकता है।

मुझे बताएं कि क्या आपके पास कोई समाधान है जिसे आप परीक्षण या अपडेट करना चाहते हैं।

सेट अप

import pandas as pd
import random

ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'

def random_letters(n, n_letters=52):
    return random.sample(ALPHABET[:n_letters], n)

# Create 12k rows to test scaling.
df = pd.DataFrame([{'id': i, 'list_of_value': random_letters(3)} for i in range(12000)])

वर्तमान विजेता

def method_quang(df): 
    s = pd.get_dummies(df.list_of_value.explode()).sum(level=0) 
    return s.dot(s.T).div(s.sum(1)) 

%time method_quang(df)                                                                                                                                                                                                               
# CPU times: user 10.5 s, sys: 828 ms, total: 11.3 s
# Wall time: 11.3 s
# ...
# [12000 rows x 12000 columns]

दावेदार

def method_mcskinner(df):
    explode_df = df.set_index('id').list_of_value.explode().reset_index() 
    explode_df = explode_df.rename(columns={'list_of_value': 'value'}) 
    denom_df = explode_df.groupby('id').size().reset_index(name='denom') 
    numer_df = explode_df.merge(explode_df, on='value', suffixes=['', '_y']) 
    numer_df = numer_df.groupby(['id', 'id_y']).size().reset_index(name='numer') 
    calc_df = numer_df.merge(denom_df, on='id') 
    calc_df['score'] = calc_df['numer'] / calc_df['denom'] 
    return calc_df.pivot('id', 'id_y', 'score').fillna(0) 

%time method_mcskinner(df)
# CPU times: user 29.2 s, sys: 9.66 s, total: 38.9 s
# Wall time: 29.6 s
# ...
# [12000 rows x 12000 columns]
def method_rishab(df): 
    vals = [[len(set(val1) & set(val2)) / len(val1) for val2 in df['list_of_value']] for val1 in df['list_of_value']]
    return pd.DataFrame(columns=df['id'], data=vals)

%time method_rishab(df)                                                                                                                                                                                                              
# CPU times: user 2min 12s, sys: 4.64 s, total: 2min 17s
# Wall time: 2min 18s
# ...
# [12000 rows x 12000 columns]
def method_fahad(df): 
    ids = list(df['id']) 
    range_of_ids = range(len(ids)) 

    def score_calculation(s_id1,s_id2): 
        s1 = set(list(df.loc[df['id'] == ids[s_id1]]['list_of_value'])[0]) 
        s2 = set(list(df.loc[df['id'] == ids[s_id2]]['list_of_value'])[0]) 
        # Resultant calculation s1&s2 
        return round(len(s1&s2)/len(s1) , 2) 

    dic = {indexQFID:  [score_calculation(indexQFID,ind) for ind in range_of_ids] for indexQFID in range_of_ids} 
    return pd.DataFrame(dic) 

# Stopped manually after running for more than 10 minutes.

समाधान के विवरण के साथ मूल पोस्ट

pandasस्व-जुड़ने के साथ ऐसा करना संभव है ।

जैसा कि अन्य जवाबों में कहा गया है, पहला कदम डेटा को लंबे रूप में अनपैक करना है।

explode_df = df.set_index('id').list_of_value.explode().reset_index()
explode_df = explode_df.rename(columns={'list_of_value': 'value'})
explode_df
#     id value
# 0    0     a
# 1    0     b
# 2    0     c
# 3    1     d
# 4    1     b
# ...

इस तालिका से प्रति-आईडी गणना की गणना करना संभव है।

denom_df = explode_df.groupby('id').size().reset_index(name='denom')
denom_df
#    id  denom
# 0   0      3
# 1   1      3
# 2   2      3
# 3   3      3

और फिर सेल्फ-जॉइन आता है, जो valueकॉलम पर होता है । यह जोड़े प्रत्येक इंटरसेक्टिंग वैल्यू के लिए एक बार आईडी बनाते हैं, इसलिए जोड़े गए आईडी को चौराहे के आकार को प्राप्त करने के लिए गिना जा सकता है।

numer_df = explode_df.merge(explode_df, on='value', suffixes=['', '_y'])
numer_df = numer_df.groupby(['id', 'id_y']).size().reset_index(name='numer')
numer_df
#     id  id_y  numer
# 0    0     0      3
# 1    0     1      2
# 2    0     2      3
# 3    0     3      3
# 4    1     0      2
# 5    1     1      3
# ...

इन दोनों को फिर से मिलाया जा सकता है, और एक अंक की गणना की जा सकती है।

calc_df = numer_df.merge(denom_df, on='id')
calc_df['score'] = calc_df['numer'] / calc_df['denom']
calc_df
#     id  id_y  numer  denom     score
# 0    0     0      3      3  1.000000
# 1    0     1      2      3  0.666667
# 2    0     2      3      3  1.000000
# 3    0     3      3      3  1.000000
# 4    1     0      2      3  0.666667
# 5    1     1      3      3  1.000000
# ...

यदि आप मैट्रिक्स फॉर्म पसंद करते हैं, तो यह संभव है कि ए के साथ pivot। यदि डेटा विरल है तो यह एक बहुत बड़ा प्रतिनिधित्व होगा।

calc_df.pivot('id', 'id_y', 'score').fillna(0)
# id_y         0         1         2         3
# id                                          
# 0     1.000000  0.666667  1.000000  1.000000
# 1     0.666667  1.000000  0.666667  0.666667
# 2     1.000000  0.666667  1.000000  1.000000
# 3     1.000000  0.666667  1.000000  1.000000

1

यह सॉल्यूशन किसी भी आकार के डेटा और आपके या listइसके किसी भी प्रकार के मानों के साथ कुशलतापूर्वक काम करेगा , यदि कोई हो, तो पुनरावृत्ति मूल्यों का भी ध्यान रखना चाहिए।strint

# dummy data
df = pd.DataFrame({'id': [0, 1, 2, 3], 'list_of_value': [['a','b','c'],['d','b','c'], ['a','b','c'], ['a','b','c']]})
# calculating the target values using list comprehension
vals = [[len(set(val1) & set(val2)) / len(val1) for val2 in df['list_of_value']] for val1 in df['list_of_value']]
# new resultant Dataframe
df =  pd.DataFrame(columns=df['id'], data=vals)

इस मामले में, सूची समझ बेहतर प्रदर्शन करती है क्योंकि इसे सूची के एपेंड विशेषता को लोड करने और प्रत्येक पुनरावृत्ति पर इसे फ़ंक्शन के रूप में कॉल करने की आवश्यकता नहीं होती है। दूसरे शब्दों में और सामान्य तौर पर, सूची की समझ तेजी से प्रदर्शन करती है क्योंकि फ़ंक्शन के फ्रेम को निलंबित करना और फिर से शुरू करना, या अन्य मामलों में कई फ़ंक्शन मांग पर एक सूची बनाने की तुलना में धीमी हैं।

लूप के स्थान पर एक सूची समझ का उपयोग करना जो सूची का निर्माण नहीं करता है, निरर्थक मूल्यों की सूची को निरर्थक रूप से जमा करता है और फिर सूची को फेंक देता है, अक्सर सूची बनाने और निकालने के ओवरहेड के कारण धीमा होता है।

परिणाम:

id         0         1         2         3
0   1.000000  0.666667  1.000000  1.000000
1   0.666667  1.000000  0.666667  0.666667
2   1.000000  0.666667  1.000000  1.000000
3   1.000000  0.666667  1.000000  1.000000

निष्पादन समय:

import timeit

def function():
    df = pd.DataFrame({'id': [0, 1, 2, 3], 'list_of_value': [['a','b','c'],['d','b','c'], ['a','b','c'], ['a','b','c']]})
    vals = [[len(set(val1) & set(val2)) / len(val1) for val2 in df['list_of_value']] for val1 in df['list_of_value']]
    df =  pd.DataFrame(columns=df['id'], data=vals)

print(timeit.timeit(f'{function()}', number=1000000))
# 0.010986731999999999

0

आप सूची को सेट में परिवर्तित कर सकते हैं और ओवरलैप की जांच करने के लिए चौराहे फ़ंक्शन का उपयोग कर सकते हैं:

(केवल 1 लागू फ़ंक्शन का उपयोग आपके द्वारा पूछे गए :-) के रूप में किया जाता है)

(
    df.assign(s = df.list_of_value.apply(set))
    .pipe(lambda x: pd.DataFrame([[len(e&f)/len(e) for f in x.s] for e in x.s]))
)

    0           1           2           3
0   1.000000    0.666667    1.000000    1.000000
1   0.666667    1.000000    0.666667    0.666667
2   1.000000    0.666667    1.000000    1.000000
3   1.000000    0.666667    1.000000    1.000000

0

मैं productसभी संयोजनों को प्राप्त करने के लिए उपयोग करूंगा । फिर हम साथ जांच कर सकते हैं numpy.isinऔर numpy.mean:

from itertools import product
l = len(df)
new_df = pd.DataFrame(data = np.array(list(map(lambda arr: np.isin(*arr),
                                                product(df['list_of_value'],
                                                        repeat=2))))
                               .mean(axis=1).reshape(l,-1),
                      index = df['id'],
                      columns=df['id'])

id         0         1         2         3
id                                        
0   1.000000  0.666667  1.000000  1.000000
1   0.666667  1.000000  0.666667  0.666667
2   1.000000  0.666667  1.000000  1.000000
3   1.000000  0.666667  1.000000  1.000000

समय का नमूना

%%timeit
l = len(df)
new_df = pd.DataFrame(data = np.array(list(map(lambda arr: np.isin(*arr),
                                                product(df['list_of_value'],
                                                        repeat=2))))
                               .mean(axis=1).reshape(l,-1),
                      index = df['id'],
                      columns=df['id'])
594 µs ± 5.05 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

0

तेजी से होना चाहिए, सूची में डुप्लिकेट पर भी विचार करें

... import itertools
... from collections import Counter
... a=df.list_of_value.tolist()
... l=np.array([len(Counter(x[0]) & Counter(x[1]))for x in [*itertools.product(a,a)]]).reshape(len(df),-1)
... out=pd.DataFrame(l/df.list_of_value.str.len().values[:,None],index=df.id,columns=df.id)
... 
out
id         0         1         2         3
id                                        
0   1.000000  0.666667  1.000000  1.000000
1   0.666667  1.000000  0.666667  0.666667
2   1.000000  0.666667  1.000000  1.000000
3   1.000000  0.666667  1.000000  1.000000

0

हाँ! हम यहां कार्टेशियन उत्पाद की तलाश कर रहे हैं, जो इस उत्तर में दिया गया है । यह लूप या एक सूची समझ के बिना प्राप्त किया जा सकता है

आइए हमारे डेटा फ़्रेम में एक नया दोहराया गया मान जोड़ें dfताकि यह इस तरह दिखाई दे:

df['key'] = np.repeat(1, df.shape[0])
df

  list_of_values  key
0      [a, b, c]    1
1      [d, b, c]    1
2      [a, b, c]    1
3      [a, b, c]    1

अगला अपने आप में विलीन हो जाता है

merged = pd.merge(df, df, on='key')[['list_of_values_x', 'list_of_values_y']]

यह मर्ज किए गए फ़्रेम जैसा दिखता है:

   list_of_values_x list_of_values_y
0         [a, b, c]        [a, b, c]
1         [a, b, c]        [d, b, c]
2         [a, b, c]        [a, b, c]
3         [a, b, c]        [a, b, c]
4         [d, b, c]        [a, b, c]
5         [d, b, c]        [d, b, c]
6         [d, b, c]        [a, b, c]
7         [d, b, c]        [a, b, c]
8         [a, b, c]        [a, b, c]
9         [a, b, c]        [d, b, c]
10        [a, b, c]        [a, b, c]
11        [a, b, c]        [a, b, c]
12        [a, b, c]        [a, b, c]
13        [a, b, c]        [d, b, c]
14        [a, b, c]        [a, b, c]
15        [a, b, c]        [a, b, c]

तब हम प्रत्येक पंक्ति का उपयोग करके वांछित फ़ंक्शन लागू करते हैं axis=1

values = merged.apply(lambda x: np.intersect1d(x[0], x[1]).shape[0] / len(x[1]), axis=1)

वांछित प्रारूप में मान प्राप्त करने के लिए इसे फिर से आकार देना

values.values.reshape(4, 4)
array([[1.        , 0.66666667, 1.        , 1.        ],
       [0.66666667, 1.        , 0.66666667, 0.66666667],
       [1.        , 0.66666667, 1.        , 1.        ],
       [1.        , 0.66666667, 1.        , 1.        ]])

उम्मीद है की यह मदद करेगा :)

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