Nadal nie jestem biegły w scali, ale używam go do przetwarzania niektórych danych, które wczytałem z pliku do następującej struktury danych:
Map[Id, (Set[Category], Set[Tag])]
gdzie
type Id = String
type Category = String
type Tag = String
Zasadniczo każdy klawisz w Map
jest unikalnym identyfikatorem jednostki powiązanym z zestawem kategorii i zestawem znaczników.
Moje pytanie brzmi: co jest najlepsze (= najwięcej wydajny i najbardziej idiomatyczny) sposób obliczania:
- oznacza częstotliwości we wszystkich jednostkach (
type TagsFrequencies = Map[Tag, Double]
) - tagi częstotliwości na kategorię (
Map[Category, TagsFrequencies]
)
Oto moja próba:
def tagsFrequencies(tags: List[Tag]): TagsFrequencies =
tags.groupBy(t => t).map(
kv => (kv._1 -> kv._2.size.toDouble / tags.size.toDouble))
def computeTagsFrequencies(data: Map[Id, (Set[Category], Set[Tag])]): TagsFrequencies = {
val tags = data.foldLeft(List[Tag]())(
(acc, kv) => acc ++ kv._2._2.toList)
tagsFrequencies(tags)
}
def computeTagsFrequenciesPerCategory(data: Map[Id, (Set[Category], Set[Tag])]): Map[Category, TagsFrequencies] = {
def groupTagsPerCategory(data: Map[Id, (Set[Category], Set[Tag])]): Map[Category, List[Tag]] =
data.foldLeft(Map[Category, List[Tag]]())(
(acc, kv) => kv._2._1.foldLeft(acc)(
(a, category) => a.updated(category, kv._2._2.toList ++ a.getOrElse(category, Set.empty).toList)))
val tagsPerCategory = groupTagsPerCategory(data)
tagsPerCategory.map(tpc => (tpc._1 -> tagsFrequencies(tpc._2)))
}
Jako przykład rozważ
val data = Map(
"id1" -> (Set("c1", "c2"), Set("t1", "t2", "t3")),
"id2" -> (Set("c1"), Set("t1", "t4")))
następnie:
tagi częstotliwości we wszystkich jednostkach to:
Map(t3 -> 0.2, t4 -> 0.2, t1 -> 0.4, t2 -> 0.2)
a tagi częstotliwości na kategorię to:
Map(c1 -> Map(t3 -> 0.2, t4 -> 0.2, t1 -> 0.4, t2 -> 0.2), c2 -> Map(t3 -> 0.3333333333333333, t1 -> 0.3333333333333333, t2 -> 0.3333333333333333))
Odpowiedzi:
2 dla odpowiedzi № 1Oto przepisanie idiomu, niekoniecznie wydajności. Uczyniłbym twoją pierwszą metodę trochę bardziej ogólną ( Iterable
argument), użyj identity
zamiast t => t
, I użyć mapValues
:
def tagsFrequencies(tags: Iterable[Tag]): TagsFrequencies =
tags.groupBy(identity).mapValues(_.size / tags.size.toDouble)
Ponieważ teraz to trwa Iterable[Tag]
, możesz go użyć do wyczyszczenia drugiej metody:
def computeTagsFrequencies(data: Map[Id, (Set[Category], Set[Tag])]) =
tagsFrequencies(data.flatMap(_._2._2))
I podobnie dla ostatniej metody:
def computeTagsFrequenciesPerCategory(data: Map[Id, (Set[Category], Set[Tag])]) =
data.values.flatMap {
case (cs, ts) => cs.map(_ -> ts)
}.groupBy(_._1).mapValues(v => tagsFrequencies(v.flatMap(_._2)))
Żadna z tych zmian nie powinna wpływać na wydajność w żaden znaczący sposób, ale powinieneś oczywiście dokonać porównania we własnej aplikacji.