Greg_Deckler
Bueno, al comienzo de este último fin de semana del Día del Trabajo, puedo decir con seguridad que lo ÚLTIMO que pensé que estaría haciendo es codificar la lógica de coincidencia de texto difuso en DAX. Bueno aquí estamos. Esto surgió debido a este hilo aquí publicado por @Anonymous. Es un hilo bastante largo con una serie de intentos fallidos, mentir (no necesito una coincidencia difusa) y muchos de mí me quejan de que era prácticamente imposible. Resulta que no lo es y se requería una coincidencia «aproximada». De todos modos, debería saber mejor a estas alturas que realmente no hay mucho que sea imposible si tuerce el cerebro de la manera correcta. De todos modos, el objetivo era hacer coincidir el nombre del cliente con el nombre del proyecto coincidente más cercano, solo que los datos son prácticamente basura.
Esta es una versión ligeramente mejorada del hilo. Incluyó un umbral de coincidencia que puede ayudar a acelerarlo, así como servir como un umbral para la coincidencia (para que no coincida solo con la letra «M», por ejemplo).
Fuzzy =
VAR __MatchWord = MAX([Client Name])
VAR __CleanMatchThreshold = 4
VAR __KillThreshold = 3
VAR __FuzzyThreshold1 = .4
VAR __FuzzyThreshold2 = .8
VAR __WordSearchTable =
GENERATE(
'Ongoing Projects',
VAR __Word = MAXX(FILTER('Ongoing Projects',[Index]=EARLIER('Ongoing Projects'[Index])),[Ongoing Projects])
RETURN ADDCOLUMNS(GENERATESERIES(3,LEN(__Word),1),"Search",LEFT(__Word,[Value]),"Original",__Word)
)
VAR __Table =
FILTER(
ADDCOLUMNS(
__WordSearchTable,
"Match",SEARCH([Search],__MatchWord,,BLANK())
),
NOT(ISBLANK([Match]))
)
VAR __Max = MAXX(__Table,[Value])
VAR __Match = MAXX(FILTER(__Table,[Value]=__Max),[Search])
VAR __Proposed =
IF(
LEN(__Match)<=__CleanMatchThreshold,
SWITCH(TRUE(),
//__Clean2 = "ABB",__Clean2,
COUNTROWS(FILTER(__Table,[Value]=__Max))>1,BLANK(),
LEN(__Match) <= __KillThreshold,BLANK(),
LEN(__Match) = LEN(__MatchWord),__Match,
LEN(__Match)/LEN(__MatchWord)>__FuzzyThreshold1 && SEARCH(__Match,__MatchWord,,0)=1,__Match,
LEN(__Match)/LEN(__MatchWord)>__FuzzyThreshold2,__Match,
BLANK()
),
SWITCH(TRUE(),
__Match = "Blue Cross" || __Match = "Blue Cross ",__Match,
LEN(__Match)/LEN(__MatchWord)<__FuzzyThreshold2 && SEARCH(__Match,__MatchWord,,0)<>1,BLANK(),
__Match
)
)
VAR __Clean1 = IF(RIGHT(__Proposed,1)="(",LEFT(__Proposed,LEN(__Proposed)-1),__Proposed)
VAR __Clean2 = IF(RIGHT(__Clean1,1)=" ",LEFT(__Clean1,LEN(__Clean1)-1),__Clean1)
RETURN
__Clean2
Un par de notas de uso. __CleanMatchThreshold es básicamente el número de caracteres por el cual si es menor o igual a este número, se realiza un escrutinio más intenso. Por encima de este número, hacemos una verificación porcentual de caracteres coincidentes frente al nombre total del cliente solo para verificar una coincidencia. Cuando cae por debajo de __CleanMatchThreshold, verificamos si hubo múltiples coincidencias de la misma longitud y, de ser así, las descartamos. También verificamos si está por debajo o igual al __KillThreshold y, si es así, lo desechamos. Siguen más comprobaciones. Tenga en cuenta que en ambos conjuntos de verificación (por encima y por debajo de __CleanMatchThreshold) podemos introducir tantas excepciones como queramos para asegurarnos de que las coincidencias específicas caen hasta el final. Algo como esto es casi inevitable con la coincidencia aproximada. ¡¡Es «borroso» después de todo !!
¿Es verdadera lógica de coincidencia difusa? Eh. Un científico de datos de coincidencia aproximada podría objetar. También un matemático; ya sabes, porque son matemáticos y todo eso. Pero, al final, es más complejo y preciso que simplemente usar SEARCH. Si. ¿Es escalable y eficaz? ¡Probablemente no! ¿Se perdió la parte sobre el uso de DAX para hacer coincidencias de texto difuso? Hola Greg, idiota, ¿por qué no usaste la lógica de fusión difusa de Power Query? Lo intentamos; no funcionó, vete. ¡Disfrutar! 🙂
El PBIX adjunto tiene algunos intentos anteriores y demás. Puedes ver cómo evolucionó.
eyJrIjoiZjFhZjY4MGUtOWFkOS00OTNkLWJkODYtNWY2MDczMDVmYjNiIiwidCI6IjRhMDQyNzQzLTM3M2EtNDNkMi04MjdiLTAwM2Y0IJIzdis