Посчитать частоты символов в строках c#
Мне необходимо посчитать частоту символов в строках (пример, как это теоретически должно работать)
Допустим, у меня дана строка с некоторым текстом. Мне необходимо разделить её на слова, длиной key, и посчитать частоту символов в каждом столбце. Я написал код, который делит её на слова, длиной key, но дальше не знаю, как можно посчитать (ниже код, который я написал):
class Program
{
static void Main(string[] args)
{
int key = 3; //длина слов
string word = "HELLOWHELOLWH"; //само слово
Console.WriteLine(word + " = " + word.Length);
List<string> SecondWords = new List<string>(); //слова, которые чуть позже будут разделены
int temp = (int)Math.Ceiling(word.Length / (double)key); //количество "частей", на которые нужно поделить слово
for (int i = 0; i < temp; i++) //деление слова на "части"
{
if (i == temp - 1)
{
if (i * key + 1 == word.Length)
SecondWords.Add(word[i * key].ToString());
if (i * key + 2 == word.Length)
SecondWords.Add(word[i * key].ToString() + word[i * key + 1].ToString());
}
if (i != temp - 1)
SecondWords.Add(word[i * key].ToString() + word[i * key + 1].ToString() + word[i * key + 2].ToString());
}
foreach (string p in SecondWords) //вывод полученных "частей" слова
{
Console.WriteLine(p);
}
Console.ReadKey();
}
}
Подскажите, пожалуйста, как это можно реализовать.
P.S. Допустим, на данном примере, частоты букв (для 1 столбика) a = 0, b = 0, c = 0, ...., h = 0,6, .... и т.д.
Ответы (3 шт):
Вариант через LINQ:
Enumerable.Range(0, (int)Math.Ceiling((decimal)str.Length / wordLen)) // Получим число слов
.Select(i => str.Substring(i * wordLen, Math.Min(str.Length-i*wordLen,wordLen)).ToCharArray())// Разобьём строку на части нужной длины (последнее слово - остатком)
.SelectMany(x => x.Select((c,i) => new { index = i, chr = c }))// Разобьём части на символы с запоминанием позиции
.GroupBy(x => x.index) // Сгруппируем в "слова"
.Select((x, i) => new { Word = i, letters = x.GroupBy(g => g.chr) }) //Сгруппируем каждое слово посимвольно
.Select(x => x.letters.Select(l => new { x.Word, l.Key, Count = l.Count() })) //Посчитаем символы
.SelectMany(x => Enumerable.Range('A', 'Z'-'A').Select(e => new //Получим алфавит
{
x.First().Word,
Letter = (char)e,
Rate = (decimal)(x.FirstOrDefault(w => w.Key == e)?.Count??0) / x.Sum(w => w.Count) //Посчитаем рейт
})).Where(x=>x.Rate>0).ToList() //Выведем все записи с ненулевым рейтом
Если нужен весь алфавит (с нулевыми записями) - последнее условие убираем.
Протестить можно тут: https://ideone.com/9QPNjL
Простейший алгоритм, переводим из 2 мерных координат в одномерные и ищем нужный символ
var len = 3; //длина слов
var words = "HELLOWHELOLWH"; //само слово
var dicts = new Dictionary<char, int>[len];
for (int i = 0; i < len; i++) dicts[i] = new Dictionary<char, int>();
var totalWords = words.Length / len + 1;
for (int i = 0; i < len; i++)
{
for (int j = 0; j < totalWords; j++)
{
var ind = j * len + i;
if (ind >= words.Length) break;
var c = words[ind];
if (dicts[i].ContainsKey(c)) dicts[i][c]++;
else dicts[i][c] = 1;
}
}
for (int i = 0; i < len; i++)
{
double total = dicts[i].Values.Sum();
foreach (var kv in dicts[i]) Console.WriteLine($"column: {i} char: {kv.Key} freq: {kv.Value / total} ({kv.Value} / {total})");
}
Результат
column: 0 char: H freq: 0.6 (3 / 5)
column: 0 char: L freq: 0.2 (1 / 5)
column: 0 char: O freq: 0.2 (1 / 5)
column: 1 char: E freq: 0.5 (2 / 4)
column: 1 char: O freq: 0.25 (1 / 4)
column: 1 char: L freq: 0.25 (1 / 4)
column: 2 char: L freq: 0.5 (2 / 4)
column: 2 char: W freq: 0.5 (2 / 4)
Чисто для галочки, linq вариант
var len = 3; //длина слов
var words = "HELLOWHELOLWH"; //само слово
var data = words.Select((x,i)=> (col:i%len, val:x))
.GroupBy(x=>x.col, x=>x.val)
.Select(x=>(col:x.Key, data: x.GroupBy(z=>z).Select(z=>(val:z.Key, count:z.Count()))));
foreach (var column in data)
{
double total = column.data.Sum(z=>z.count);
foreach (var item in column.data)
Console.WriteLine($"column: {column.col} char: {item.val} freq: {item.count / total} ({item.count} / {total})");
}
Вывод
column: 0 char: H freq: 0.6 (3 / 5)
column: 0 char: L freq: 0.2 (1 / 5)
column: 0 char: O freq: 0.2 (1 / 5)
column: 1 char: E freq: 0.5 (2 / 4)
column: 1 char: O freq: 0.25 (1 / 4)
column: 1 char: L freq: 0.25 (1 / 4)
column: 2 char: L freq: 0.5 (2 / 4)
column: 2 char: W freq: 0.5 (2 / 4)
