55import numpy as np
66import upsetplot as up
77from rapidfuzz import fuzz
8- from .DATA import sparql_to_dataframe
8+ from .DATA import sparql_to_dataframe , get_token_matrix
99
1010def correlation (df = None ,
1111 endpoint_url = None ,
1212 query = None ,
13- col1 = None , col2 = None , sep = ';' , edges = 0 , csv_filename = "correlations.csv" , heatmap = True , heatmap_kwargs = {}, save_PNG = True , verbose = True ):
13+ col1 = None , col2 = None , sep = ';' , edges = 0 , csv_filename = "correlations.csv" , heatmap = True , heatmap_kwargs = {}, dummies_matrix = True , save_PNG = True , verbose = True ):
1414 """
1515 Compute correlations between two columns of a DataFrame, including support for categorical (string) data.
1616
@@ -27,6 +27,7 @@ def correlation(df=None,
2727 csv_filename (str, optional): File path to save the result as CSV.
2828 heatmap (bool, optional): Whether to generate a heatmap. Default is True.
2929 heatmap_kwargs (dict, optional): Additional kwargs passed to the heatmap function.
30+ dummies_matrix (bool, optional): Treat col1 and col2 as binary dummies. Default is True, else bag of words, count frequency.
3031 save_PNG (bool, optional): Whether to save the heatmap as a PNG file.
3132 verbose (bool, optional): Whether to print insights into the dataframe.
3233
@@ -55,8 +56,9 @@ def correlation(df=None,
5556 return pd .DataFrame ({'Correlation' : [correlation ], 'P-Value' : [p_val ]}, index = [f'{ col1 } vs { col2 } ' ])
5657 elif pd .api .types .is_string_dtype (df [col1 ]):
5758 # Both are string, create dummy variables once
58- dummies = df [col1 ].str .get_dummies (sep = sep )
59-
59+ dummies = get_token_matrix (df [col1 ],sep ,dummies_matrix )
60+ # dummies = df[col1].str.get_dummies(sep=sep)
61+
6062 if dummies .shape [1 ] < 2 :
6163 raise ValueError (f"Not enough dummy variables in '{ col1 } ' to calculate correlations." )
6264
@@ -70,12 +72,12 @@ def correlation(df=None,
7072 # Case: Both columns are different
7173
7274 if pd .api .types .is_string_dtype (df [col1 ]):
73- col1_dummies_raw = df [col1 ].astype (str ).str .get_dummies (sep = sep )
75+ col1_dummies_raw = get_token_matrix ( df [ col1 ], sep , dummies_matrix ) # df[col1].astype(str).str.get_dummies(sep=sep)
7476 else :
7577 col1_dummies_raw = df [[col1 ]].astype (float )
7678
7779 if pd .api .types .is_string_dtype (df [col2 ]):
78- col2_dummies_raw = df [col2 ].astype (str ).str .get_dummies (sep = sep )
80+ col2_dummies_raw = get_token_matrix ( df [ col2 ], sep , dummies_matrix ) # df[col2].astype(str).str.get_dummies(sep=sep)
7981 else :
8082 col2_dummies_raw = df [[col2 ]].astype (float )
8183
@@ -256,9 +258,10 @@ def upset(
256258 return df_final
257259
258260def fuzzy_compare (df1 = None ,df2 = None ,
259- endpoint_url = None ,
260- query = None ,
261- grouping_var = None , label_var = None , element_var = None , threshold = 95 , match_all = False , unique_rows = False , csv_filename = "comparison.csv" , verbose = True ):
261+ additional_vars_df1 = None , additional_vars_df2 = None ,
262+ endpoint_url = None ,
263+ query = None ,
264+ grouping_var = None , label_var = None , element_var = None , threshold = 95 , match_all = False , unique_rows = False , csv_filename = "comparison.csv" , verbose = True ):
262265 """
263266 Fuzzy string matching between two DataFrames (or SPARQL query results) based on a common element column.
264267
@@ -267,6 +270,8 @@ def fuzzy_compare(df1=None,df2=None,
267270 Args:
268271 df1 (pd.DataFrame, optional): First DataFrame.
269272 df2 (pd.DataFrame, optional): Second DataFrame. If not provided, df1 is used.
273+ additional_vars_df1 (list, optional): List of columns that will be aggregated in the result (using first per group).
274+ additional_vars_df2 (list, optional): List of columns that will be aggregated in the result (using first per group).
270275 endpoint_url (str, optional): SPARQL endpoint.
271276 query (str, optional): SPARQL query.
272277 grouping_var (str, optional): Column name used for grouping.
@@ -283,23 +288,28 @@ def fuzzy_compare(df1=None,df2=None,
283288 """
284289
285290
286- if df1 is None and endpoint_url and query :
287- try :
288- # Fetch data and create DataFrame
289- df1 = sparql_to_dataframe (endpoint_url , query , csv_filename = f"query1_{ csv_filename } " if csv_filename is not None else None )
290- except Exception as e :
291- raise ValueError (f"Failed to fetch or process SPARQL query results. Error: { e } " )
292291
293- if df2 is None and endpoint_url and query :
292+ if df1 is None and endpoint_url and query :
294293 try :
295294 # Fetch data and create DataFrame
296- df2 = sparql_to_dataframe (endpoint_url , query , csv_filename = f"query2_ { csv_filename } " if csv_filename is not None else None )
295+ df1 = sparql_to_dataframe (endpoint_url , query , csv_filename = f"query_ { csv_filename } " if csv_filename is not None else None )
297296 except Exception as e :
298297 raise ValueError (f"Failed to fetch or process SPARQL query results. Error: { e } " )
299298
300299 if df2 is None :
301300 df2 = df1
302301
302+
303+ if additional_vars_df1 is None :
304+ additional_vars_df1 = []
305+ else :
306+ additional_vars_df1 = [col for col in additional_vars_df1 if col in df1 .columns ]
307+
308+ if additional_vars_df2 is None :
309+ additional_vars_df2 = []
310+ else :
311+ additional_vars_df2 = [col for col in additional_vars_df2 if col in df2 .columns ]
312+
303313 # Validate that columns exist in the DataFrame
304314 if element_var not in df1 .columns or element_var not in df2 .columns :
305315 raise ValueError (f"Column '{ element_var } ' do not exist in the DataFrame." )
@@ -344,14 +354,22 @@ def fuzzy_compare(df1=None,df2=None,
344354 else :
345355 score = fuzz .ratio (row2 [element_var ].lower (), row1 [element_var ].lower ())
346356
347- matches . append ( {
357+ match_entry = {
348358 'group2' : group_key2 ,
349359 'group1' : group_key1 ,
350360 'label' : row2 [label_var ] if check_label else None ,
351361 'element2' : row2 [element_var ],
352362 'element1' : row1 [element_var ],
353363 'score' : score
354- })
364+ }
365+
366+ for col in additional_vars_df1 :
367+ match_entry [f'df1_{ col } ' ] = row1 [col ]
368+
369+ for col in additional_vars_df2 :
370+ match_entry [f'df2_{ col } ' ] = row2 [col ]
371+
372+ matches .append (match_entry )
355373
356374 matches_df = pd .DataFrame (matches )
357375 aggregated = pd .DataFrame ()
@@ -363,15 +381,23 @@ def fuzzy_compare(df1=None,df2=None,
363381 matches_df = matches_df [matches_df ['score' ] >= threshold ]
364382
365383 if not matches_df .empty :
366- aggregated = matches_df .groupby (['group1' , 'group2' ]).agg (
367- Labels = ('label' , lambda x : ", " .join (sorted (set (x )))),
368- df1_Elements = ('element1' , lambda x : ", " .join (sorted (set (x )))),
369- df2_Elements = ('element2' , lambda x : ", " .join (sorted (set (x )))),
370- Num_Matches = ('score' , 'count' ),
371- Average_Score = ('score' , 'mean' ),
372- Min_Score = ('score' , 'min' ),
373- Max_Score = ('score' , 'max' )
374- ).reset_index ()
384+ agg_dict = {
385+ 'Labels' : ('label' , lambda x : ", " .join (sorted (set (x )))),
386+ 'df1_Elements' : ('element1' , lambda x : ", " .join (sorted (set (x )))),
387+ 'df2_Elements' : ('element2' , lambda x : ", " .join (sorted (set (x )))),
388+ 'Num_Matches' : ('score' , 'count' ),
389+ 'Average_Score' : ('score' , 'mean' ),
390+ 'Min_Score' : ('score' , 'min' ),
391+ 'Max_Score' : ('score' , 'max' )
392+ }
393+
394+ for col in additional_vars_df1 :
395+ agg_dict [f'df1_{ col } ' ] = (f'df1_{ col } ' , 'first' )
396+ for col in additional_vars_df2 :
397+ agg_dict [f'df2_{ col } ' ] = (f'df2_{ col } ' , 'first' )
398+
399+ aggregated = matches_df .groupby (['group1' , 'group2' ]).agg (** agg_dict ).reset_index ()
400+
375401 if verbose :
376402 print (aggregated .info ())
377403 aggregated .describe ()
0 commit comments