程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

拯救pandas計劃(19)——使用自定義方法計算兩列的相似度

編輯:Python

拯救pandas計劃(19)——使用自定義方法計算兩列的相似度

最近發現周圍的很多小伙伴們都不太樂意使用pandas,轉而投向其他的數據操作庫,身為一個數據工作者,基本上是張口pandas,閉口pandas了,故而寫下此系列以讓更多的小伙伴們愛上pandas。

系列文章說明:

系列名(系列文章序號)——此次系列文章具體解決的需求

平台:

  • windows 10

  • python 3.8

  • pandas >=1.2.4

/ 數據需求

最近在互聯網遨游時偶然發現一道很有趣的pandas實例訓練題目,題目距今已發布很久,也已經有人為此解答過了,說:有一組數據,每列分別記錄著樣品的各個指標情況,欲對每個數據各個指標進行計算,求各個樣品之間的指標差值的絕對值的總和最小的對應的幾個樣品,如:a樣品:[10.0, 9.0, 7.5, 8.6]b樣品:[11.2, 8.7, 6.4, 5.5]之間的相似度計算為:|10.0-11.2|+|9.0-8.7|+|7.5-6.4|+|8.6-5.5|

對應位置相減取絕對值後再相加,為每個樣品對其余樣品進行計算,求得所有樣品的與其相似度最小的5個其他樣品。

根據上述描述,構造一組隨機數據:

import numpy as np
import pandas as pd
np.random.seed(2022)
data = np.clip(np.random.normal(loc=70, scale=15, size=600).round(2), 0, 100).reshape(60, 10)
df = pd.DataFrame(data, columns=list('abcdefghij'), index=[f'測試_{i}' for i in range(1, 61)])

數據大小為:60*10,浮點型類型。

/ 需求處理

  • 方法一:笛卡爾積

先計算每個樣品的交叉數據,再分組獲取組內排名在上的5個數據。

這個方法是該題目下方解答者大佬的方法,其使用的方法進行笛卡爾乘積非常有意思。

df = df.reset_index().rename(columns={'index': '名稱'})  
# 設置輔助列
df['one'] = 1
# merge自身,以one列作為key參數合並數據框
df_merge = pd.merge(left=df, right=df, left_on='one', right_on='one')

原數據大小為60*10,乘積後為3600行,內存占用大小相比處理前大了不少,如果數據過大,在處理方面,耗時上會顯著增加。

暫且不考慮數據量的影響,繼續處理。

# 剔掉名稱列和one列
columns = list(df.columns)
columns.remove('名稱')
columns.remove('one')
def sim_fun(row):
    sim_value = 0.0
    for col in columns:
        sim_value += abs(round(row[col+'_x'] - row[col+'_y'], 2))
    return round(sim_value, 2)
# 求出每個樣品之間的相似值
df_merge['sim'] = df_merge.apply(sim_fun, axis=1)
df_merge = df_merge[df_merge['名稱_x'] != df_merge['名稱_y']].copy()
# 獲取每個樣品與其相似值最小的5個樣品
def get_top_sims(df_sub):
    df_sort = df_sub.sort_values('sim').head(5)
    names = ','.join(df_sort['名稱_y'])
    sims = ','.join(df_sort['sim'].astype(str))
    return pd.Series({'names': names, 'sims': sims})
df_result = df_merge.groupby('名稱_x').apply(get_top_sims)

通過復現解答大佬的代碼如上,順利返回了相似度前五的數據框。可以看到在60條數據中使用笛卡爾積進行計算,耗用時間是可以接受的。

  • 方法二:將每一行數據與原數據直接相減,利用pandas特性求和

在方法一中能注意到數據處理上稍有繁瑣,將數據擴容再處理,且數據的差值為逐個計算。仔細理解數據,可以利用數組的廣播特性進行處理。

def get_sims(s):
    # 注意:此處的df為全局變量
    df_sim = (df - s).abs().sum(axis=1).round(2)
    df_sim.pop(s.name)
    df_sim = df_sim.sort_values(ascending=False).head(5).astype(str)
    return pd.Series({'names': ','.join(df_sim.index), 'sims': ','.join(df_sim.values)})
# apply按行遍歷數據,使其他行與該行進行數據計算
df_result = df.apply(get_sims, axis=1)

使用廣播的特性將方法一中的函數優化為get_sims函數,在計算效率上也大大提高。

/ 總結

本文通過了解題目目的及其他解答者的想法,根據自身掌握的pandas技巧對題目進行分析處理,在一定程度上減少數據計算的冗余度,滿足數據需求,如對本文有不理解之處,盡可發言表述自己的想法。

獨樂樂不如眾樂樂。


於二零二二年六月二十一日作


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved