2023-07-06 10:28:53 +02:00
|
|
|
|
2021-12-27 17:35:02 +01:00
|
|
|
package eu.dnetlib.pace.tree;
|
|
|
|
|
2023-07-06 10:28:53 +02:00
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
import com.wcohen.ss.AbstractStringDistance;
|
|
|
|
|
2021-12-27 17:35:02 +01:00
|
|
|
import eu.dnetlib.pace.config.Config;
|
|
|
|
import eu.dnetlib.pace.model.Person;
|
2023-07-18 11:38:56 +02:00
|
|
|
import eu.dnetlib.pace.tree.support.AbstractListComparator;
|
2021-12-27 17:35:02 +01:00
|
|
|
import eu.dnetlib.pace.tree.support.ComparatorClass;
|
|
|
|
|
|
|
|
@ComparatorClass("authorsMatch")
|
2023-07-18 11:38:56 +02:00
|
|
|
public class AuthorsMatch extends AbstractListComparator {
|
2021-12-27 17:35:02 +01:00
|
|
|
|
2023-07-06 10:28:53 +02:00
|
|
|
Map<String, String> params;
|
|
|
|
|
|
|
|
private double SURNAME_THRESHOLD;
|
|
|
|
private double NAME_THRESHOLD;
|
|
|
|
private double FULLNAME_THRESHOLD;
|
|
|
|
private String MODE; // full or surname
|
|
|
|
private int SIZE_THRESHOLD;
|
|
|
|
private String TYPE; // count or percentage
|
|
|
|
|
|
|
|
public AuthorsMatch(Map<String, String> params) {
|
|
|
|
super(params, new com.wcohen.ss.JaroWinkler());
|
|
|
|
this.params = params;
|
|
|
|
|
|
|
|
MODE = params.getOrDefault("mode", "full");
|
|
|
|
SURNAME_THRESHOLD = Double.parseDouble(params.getOrDefault("surname_th", "0.95"));
|
|
|
|
NAME_THRESHOLD = Double.parseDouble(params.getOrDefault("name_th", "0.95"));
|
|
|
|
FULLNAME_THRESHOLD = Double.parseDouble(params.getOrDefault("fullname_th", "0.9"));
|
|
|
|
SIZE_THRESHOLD = Integer.parseInt(params.getOrDefault("size_th", "20"));
|
|
|
|
TYPE = params.getOrDefault("type", "percentage");
|
|
|
|
}
|
|
|
|
|
|
|
|
protected AuthorsMatch(double w, AbstractStringDistance ssalgo) {
|
|
|
|
super(w, ssalgo);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2023-07-18 11:38:56 +02:00
|
|
|
public double compare(final List<String> a, final List<String> b, final Config conf) {
|
2023-07-06 10:28:53 +02:00
|
|
|
if (a.isEmpty() || b.isEmpty())
|
|
|
|
return -1;
|
|
|
|
|
2023-07-18 11:38:56 +02:00
|
|
|
if (a.size() > SIZE_THRESHOLD || b.size() > SIZE_THRESHOLD)
|
2023-07-06 10:28:53 +02:00
|
|
|
return 1.0;
|
|
|
|
|
2023-10-02 09:25:12 +02:00
|
|
|
int maxMiss = Integer.MAX_VALUE;
|
2023-07-18 11:38:56 +02:00
|
|
|
List<Person> bList = b.stream().map(author -> new Person(author, false)).collect(Collectors.toList());
|
2023-07-06 10:28:53 +02:00
|
|
|
|
2023-10-02 09:25:12 +02:00
|
|
|
Double threshold = getDoubleParam("threshold");
|
|
|
|
|
|
|
|
if (threshold != null && threshold >= 0.0 && threshold <= 1.0 && a.size() == b.size()) {
|
|
|
|
maxMiss = (int) Math.floor((1 - threshold) * Math.max(a.size(), b.size()));
|
|
|
|
}
|
|
|
|
|
|
|
|
int common = 0;
|
2023-07-06 10:28:53 +02:00
|
|
|
// compare each element of List1 with each element of List2
|
2023-10-02 09:25:12 +02:00
|
|
|
for (int i = 0; i < a.size(); i++) {
|
|
|
|
Person p1 = new Person(a.get(i), false);
|
2023-07-06 10:28:53 +02:00
|
|
|
|
|
|
|
for (Person p2 : bList) {
|
|
|
|
// both persons are inaccurate
|
|
|
|
if (!p1.isAccurate() && !p2.isAccurate()) {
|
|
|
|
// compare just normalized fullnames
|
|
|
|
String fullname1 = normalization(
|
|
|
|
p1.getNormalisedFullname().isEmpty() ? p1.getOriginal() : p1.getNormalisedFullname());
|
|
|
|
String fullname2 = normalization(
|
|
|
|
p2.getNormalisedFullname().isEmpty() ? p2.getOriginal() : p2.getNormalisedFullname());
|
|
|
|
|
|
|
|
if (ssalgo.score(fullname1, fullname2) > FULLNAME_THRESHOLD) {
|
|
|
|
common += 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// one person is inaccurate
|
|
|
|
if (p1.isAccurate() ^ p2.isAccurate()) {
|
|
|
|
// prepare data
|
|
|
|
// data for the accurate person
|
|
|
|
String name = normalization(
|
|
|
|
p1.isAccurate() ? p1.getNormalisedFirstName() : p2.getNormalisedFirstName());
|
|
|
|
String surname = normalization(
|
|
|
|
p1.isAccurate() ? p1.getNormalisedSurname() : p2.getNormalisedSurname());
|
|
|
|
|
|
|
|
// data for the inaccurate person
|
|
|
|
String fullname = normalization(
|
|
|
|
p1.isAccurate()
|
|
|
|
? ((p2.getNormalisedFullname().isEmpty()) ? p2.getOriginal() : p2.getNormalisedFullname())
|
|
|
|
: (p1.getNormalisedFullname().isEmpty() ? p1.getOriginal() : p1.getNormalisedFullname()));
|
|
|
|
|
|
|
|
if (fullname.contains(surname)) {
|
|
|
|
if (MODE.equals("full")) {
|
|
|
|
if (fullname.contains(name)) {
|
|
|
|
common += 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else { // MODE equals "surname"
|
|
|
|
common += 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// both persons are accurate
|
|
|
|
if (p1.isAccurate() && p2.isAccurate()) {
|
|
|
|
|
|
|
|
if (compareSurname(p1, p2)) {
|
|
|
|
if (MODE.equals("full")) {
|
|
|
|
if (compareFirstname(p1, p2)) {
|
|
|
|
common += 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else { // MODE equals "surname"
|
|
|
|
common += 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2023-10-02 09:25:12 +02:00
|
|
|
}
|
2023-07-06 10:28:53 +02:00
|
|
|
|
2023-10-02 09:25:12 +02:00
|
|
|
if (i - common > maxMiss) {
|
|
|
|
return 0.0;
|
2023-07-06 10:28:53 +02:00
|
|
|
}
|
2023-10-02 09:25:12 +02:00
|
|
|
}
|
2023-07-06 10:28:53 +02:00
|
|
|
|
|
|
|
// normalization factor to compute the score
|
2023-10-02 09:25:12 +02:00
|
|
|
int normFactor = a.size() == b.size() ? a.size() : (a.size() + b.size() - common);
|
2023-07-06 10:28:53 +02:00
|
|
|
|
|
|
|
if (TYPE.equals("percentage")) {
|
|
|
|
return (double) common / normFactor;
|
|
|
|
} else {
|
|
|
|
return (double) common;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean compareSurname(Person p1, Person p2) {
|
|
|
|
return ssalgo
|
|
|
|
.score(
|
|
|
|
normalization(p1.getNormalisedSurname()), normalization(p2.getNormalisedSurname())) > SURNAME_THRESHOLD;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean compareFirstname(Person p1, Person p2) {
|
|
|
|
|
|
|
|
if (p1.getNormalisedFirstName().length() <= 2 || p2.getNormalisedFirstName().length() <= 2) {
|
|
|
|
if (firstLC(p1.getNormalisedFirstName()).equals(firstLC(p2.getNormalisedFirstName())))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ssalgo
|
|
|
|
.score(
|
|
|
|
normalization(p1.getNormalisedFirstName()),
|
|
|
|
normalization(p2.getNormalisedFirstName())) > NAME_THRESHOLD;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String normalization(String s) {
|
|
|
|
return normalize(utf8(cleanup(s)));
|
|
|
|
}
|
2021-12-27 17:35:02 +01:00
|
|
|
|
|
|
|
}
|