【问题标题】:Words frequency in percentage javajava中的词频百分比
【发布时间】:2015-05-05 12:42:29
【问题描述】:

我必须编写一个程序来处理链表中的单词频率并输出如下结果: 单词,出现次数,频率百分比

import java.io.File;
import java.io.FileNotFoundException;
import java.util.*;

public class Link {

    public static void main(String args[]) {

    long start = System.currentTimeMillis();

    LinkedList<String> list = new LinkedList<String>();

    File file = new File("words.txt");

    try {

        Scanner sc = new Scanner(file);

        String words;

        while (sc.hasNext()) {
            words = sc.next();
            words = words.replaceAll("[^a-zA-Z0-9]", "");
            words = words.toLowerCase();
            words = words.trim();
            list.add(words);
        }

        sc.close();

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }

    Map<String, Integer> frequency = new TreeMap<String, Integer>();

    for (String count : list) {
        if (frequency.containsKey(count)) {
            frequency.put(count, frequency.get(count) + 1);
        } else {
            frequency.put(count, 1);
        }
    }

    System.out.println(frequency);

    long end = System.currentTimeMillis();

    System.out.println("\n" + "Duration: " + (end - start) + " ms");
    }
}

输出:{a=1, ab=3, abbc=1, asd=2, xyz=1}

我不知道如何以百分比计算频率并忽略少于 2 个字符的单词。例如“a=1”应该被忽略。

提前致谢。

【问题讨论】:

  • 注意:尽管您是从 Java 开始的 - 尝试将所有代码都填充到 main.xml 中。一个函数/方法应该做一个的事情,而不是两个、三个或 10 个。回答你的问题:你真的需要 Stackoverflow 来向你介绍 if/then/else 的概念吗?例如:if (word has less than 2 characters),那么不加到词表中?!
  • 是否应该在频率中忽略少于2个字符的单词?我的意思是:“aa”应该输出 {aa=0.5} 或 {a=1.0} ?
  • 少于 2 个字符的单词不会算作一个单词,或者你会算他们但不会输入输出。?

标签: java


【解决方案1】:

首先,引入一个double 变量来跟踪出现的总数。例如

double total = 0;

接下来是用length() &lt; 2 过滤掉任何String。在将它们添加到您的 LinkedList 之前,您已经可以这样做了。

while (sc.hasNext()) {
    words = sc.next();
    words = words.replaceAll("[^a-zA-Z0-9]", "");
    words = words.toLowerCase();
    words = words.trim();
    if (words.length() >= 2) list.add(words); //Filter out strings < 2 chars
}

现在,当检查您的Strings 时,我们应该像这样每次出现都将total 变量增加1

for (String count : list) {
    if (frequency.containsKey(count)) {
        frequency.put(count, frequency.get(count) + 1);
    } else {
        frequency.put(count, 1);
    }
    total++; //Increase total number of occurences
}

然后我们可以使用System.out.printf() 很好地打印出来。

for (Map.Entry<String, Integer> entry: frequency.entrySet()) {
    System.out.printf("String: %s \t Occurences: %d \t Percentage: %.2f%%%n", entry.getKey(), entry.getValue(), entry.getValue()/total*100);
}




请注意,一旦您使用大型 Strings 或发生大量事件,这将看起来不太好(printf 语句)。因此,鉴于maxLength 包含列表中所有String 中最大的length(),并且occLength 包含出现次数最多的位数,您可以选择执行以下操作。

for (Map.Entry<String, Integer> entry: frequency.entrySet()) {
    System.out.printf("String: %" + maxLength + "s  Occurences: %" + occLength + "d  Percentage: %.2f%%%n", entry.getKey(), entry.getValue(), entry.getValue()/total*100);
}


【讨论】:

  • 似乎您可以删除第一个循环并在计算单词时过滤它们。对于一个小案例,它并没有太大的区别,但不必遍历整个单词列表两次。
  • 确实如此。毫无疑问,代码(在许多方面)可以设计得更好、更高效。这只是对他的问题的直截了当的回答。
【解决方案2】:

在添加到映射步骤时忽略大小小于 2 的字符串,并维护一个合法的单词计数器来计算百分比。

int legalWords = 0;
for (String count: list) {
    if (count.size() >= 2) {
        if (frequency.containsKey(count)) {
            frequency.put(count, frequency.get(count) + 1);
        } else {
            frequency.put(count, 1);
        }
        legalWords++;
    }
}
for (Map.Entry < String, String > entry: map.entrySet()) {
    System.out.println(entry.getKey() + " " + entry.getValue() + " " + (entry.getValue() / (double) legalWords) * 100.0 + "%");
}

【讨论】:

    【解决方案3】:

    注意:由于OP问题没有提供详细信息,我们假设我们将计算一个字符的单词但我们不会输出它们。

    将你的逻辑与你的主类分开:

    class WordStatistics {
        private String word;
        private long occurrences;
        private float frequency;
    
        public WordStatistics(String word){
            this.word=word;
        }
    
        public WordStatistics calculateOccurrences(List<String> words) {
            this.occurrences = words.stream()
                    .filter(p -> p.equalsIgnoreCase(this.word)).count();
            return this;
       }
    
        public WordStatistics calculateFrequency(List<String> words) {
            this.frequency = (float) this.occurrences / words.size() * 100;
            return this;
        }
    
        // getters and setters
    
    }
    

    说明:

    考虑到这个单词列表:

    List<String> words = Arrays.asList("Java", "C++", "R", "php", "Java",
            "C", "Java", "C#", "C#","Java","R");
    

    使用 java 8 Streams API 计算 wordsword 的出现次数:

       words.stream()
                .filter(p -> p.equalsIgnoreCase(word)).count();
    

    计算单词的频率百分比:

      frequency = (float) occurrences / words.size() * 100;
    

    设置单词的统计信息(出现次数+频率):

    List<WordStatistics> wordsStatistics = new LinkedList<WordStatistics>();
    
        words.stream()
                .distinct()
                .forEach(
                        word -> wordsStatistics.add(new WordStatistics(word)
                                .calculateOccurrences(words)
                                .calculateFrequency(words)));
    

    忽略一个字符的单词的输出结果:

        wordsStatistics
                .stream()
                .filter(word -> word.getWord().length() > 1)
                .forEach(
                        word -> System.out.printf("Word : %s \t"
                                + "Occurences : %d \t"
                                + "Frequency : %.2f%% \t\n", word.getWord(),
                                word.getOccurrences(), word.getFrequency()));
    

    输出:

    Word : C#       Occurences : 2  Frequency : 18.18%  
    Word : Java     Occurences : 4  Frequency : 36.36%  
    Word : C++      Occurences : 1  Frequency : 9.09%   
    Word : php      Occurences : 1  Frequency : 9.09% 
    

    【讨论】:

    • 所有这些链接和 lambda 都会导致一些令人不快的缩进。我很难相信这就是这些工具的真正用途。
    • 当结合多个功能,如 remove 重复的单词和 loop 遍历列表并设置WordStatistics 的列表时,它似乎是有点长。 @bhspencer
    • 也许这可以通过遵循流链和 lambda 的一些代码格式标准来改进,但就目前而言,我发现它几乎不可读。
    • @bhspencer 我明白你的意思,你是绝对正确的。代码必须对人们来说是可读且明显的。那么现在怎么样(答案已编辑)?
    • 这样干净多了。谢谢。
    【解决方案4】:

    使用简单的数据结构使这更容易。

    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;
    
    public class WordCounter {
    
        private int wordTotal;
        private Map<String, Integer> wordCount = new HashMap<>();
    
        public WordCounter(List<String> words) {
    
            wordTotal = words.size();
    
            for (String word : words) {
                wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
            }
        }
    
        public Map<String, Double> getPercentageByWord() {
    
            return wordCount.entrySet()
                    .stream()
                    .collect(Collectors.toMap(e -> e.getKey(),
                            e -> getPercentage(e.getValue())));
        }
    
        private double getPercentage(double count) {
            return (count / wordTotal) * 100;
        }
    }
    

    这是一个使用它的测试。

    @Test
    public void testWordCount() {
    
        List<String> words = Arrays.asList("a", "a", "a", "a", "b", "b", "c", "d", "e", "f");
    
        WordCounter counter = new WordCounter(words);
        Map<String, Double> results = counter.getPercentageByWord();
    
        assertThat(results).hasSize(6);
        assertThat(results).containsEntry("a", 40.0);
        assertThat(results).containsEntry("b", 20.0);
        assertThat(results).containsEntry("c", 10.0);
        assertThat(results).containsEntry("d", 10.0);
        assertThat(results).containsEntry("e", 10.0);
        assertThat(results).containsEntry("f", 10.0);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-29
      • 2019-01-12
      • 1970-01-01
      相关资源
      最近更新 更多