একটি কলাম প্যান্ডাস থেকে একটি এনএক্সএন ম্যাট্রিক্স তৈরি করুন


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
প্রশ্নটি সম্পাদনা করেছেন, @ বেবিডেস্তা
শ্রীরাম অরবিন্দ লক্ষ্মণকুমার

1
এটি 6 নয়, এটি 0.6, ফলস্বরূপ ize আকার = 2, আইডি.সাইজ = 3
শ্রীরাম অরবিন্দ লক্ষ্মণকুমার

আপনার ডেটা কত দিন? এবং সম্পূর্ণরূপে কতগুলি মান ঘটে 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))সারি দ্বারা গণনা বিভক্ত।


12 কে সারি ডাটাফ্রেম
শ্রীরাম অরবিন্দ লক্ষ্মণকুমার

@ শ্রীরাম অরবিন্দলক্ষ্মণকুমার ১২ কেও সারি সহ, আপনি 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

আপডেট করা হয়েছে

যেহেতু প্রচুর প্রার্থীর সমাধান প্রস্তাবিত, তাই সময় বিশ্লেষণ করা ভাল ধারণা বলে মনে হয়। আমি ওপি কর্তৃক অনুরোধ অনুসারে 12 কে সারি সহ কিছু এলোমেলো তথ্য উত্পন্ন করেছি, প্রতি সেট প্রতি 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বলে তার strবা intবা অন্যথায়, তার সাথে সাথে যদি পুনরাবৃত্তিমূলক মান তদারক।

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

আশাকরি এটা সাহায্য করবে :)

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