티스토리 뷰

Angelo님의 블로그에서 "꿈틀 꿈틀 움직이는 바 그래프를 그려봅시다"라는 글을 본 적이 있다. 신기해서 나도 써먹어보려고 했으나 마땅히 적용할 데이터가 없어서(...) 미루고 있던 차에, 단톡방 통계를 내 달라는 퀘스트를 받았다. 그냥 특정 기간 동안 말 많이 한 사람을 추려내는 건 정규식 지옥만 빼면 쉽다. 하지만 이왕이면 꿈틀 꿈틀 움직이는 그래프면 더 재밌지 않겠는가.
그런데 해당 글을 다시 보니 문제가 있음을 깨달았다. 오리지날 코드는 데이터가 아래와 같이 timetable 형태여야 한다.

위 데이터는 일본의 각 현의 인구수 변화이다. 첫 번째 column은 시간(년도)이고, 두 번째부터는 시간에 대한 인구수 데이터이다. 반면 단톡방 대화내역은 아래처럼 생겼다.

두 가지 작업을 해야 한다.
1. 하나의 챗은 항상 "[username] [오"로 시작한다. 그 외에는 무시할 수 있다. 이 패턴으로 시작하는 줄만 찾아내서 username을 추출해야 한다.
2. 이것을 timetable 형태로 만들어야 한다.
1번은 정규식 지옥일 뿐 쉽다. 2번이 문제다. Username을 쫙 펼쳐놓고 하나의 챗마다 누적 카운트를 주면 너무 데이터가 많아진다. 그래서 적당히 묶어서 카운트를 하고 (예: 챗 1000개마다 업데이트) timetable로 만들기로 했다.
우선 파일을 불러온다.
clear
close all
clc
folder = 'D:\SHKang\lazyMatlab\chat';
file = 'KakaoTalk_20250825_1726_38_711_group.txt';
fid = fopen(fullfile(folder, file));
s = fscanf(fid, '%c');
fclose(fid);
s = split(s, newline);
s = string(s);
split(s, newline)을 하면 줄넘김을 기준으로 cell array가 만들어진다. 이것을 다루기 편한 string으로 바꾸었다.
이제 "[username][오"로 시작하는 줄을 찾아서 username만 순서대로 남긴다.
%% username만 순서대로 남기기
usernames = strings(length(s), 1);
for i = 1:length(s)
name = regexp(s(i), '^\[(.*?)\] \[오', 'tokens');
if ~isempty(name)
usernames(i) = name{1};
end
end
usernames(usernames == "") = [];
정규식 설명은 생략한다. (사실 챗gpt 시켰다.)
마지막에는 빈 string을 없애고 username만 등장 순서대로 남겼다.
그래프 없이 단순히 랭킹만 보고 싶다면 아래 코드만 실행하면 된다.
%% 랭킹 보기
[allNames, ~, ic] = unique(usernames, 'stable');
counts = accumarray(ic, 1);
freqTable = table(allNames, counts, 'VariableNames', {'Username','Count'});
freqTable = sortrows(freqTable, 'Count', 'descend');
head(freqTable)

위 코드를 돌리면 allNames에 모든 username이 등장 순서대로 한 번씩 들어간다. 이제 이걸 timetable로 만들어야 한다. 먼저 첫 번째 column에 username만 채워놓은 테이블을 만든다.
raceTable = table(allNames, 'VariableNames', {'Username'});
챗을 몇 개씩 묶어서 카운트를 할지 정해야 하는데, 이번 건은 챗 개수가 총 13만개였으므로 1000개씩 끊어서 세자. 아래 코드를 통해 1000개씩 묶어서 카운트 한 데이터가 만들어진다.
%% ready, get set
raceTable = table(allNames, 'VariableNames', {'Username'});
Nchunk = 1000;
for i = 1:Nchunk:length(usernames)
iEnd = min(i + Nchunk - 1, length(usernames));
idx = i:iEnd;
varname = idx(1) + "~" + idx(end);
raceTable.(varname) = countcats(...
categorical(usernames(idx), raceTable.Username));
end

우리가 보려는 것은 누적 데이터이다. cumsum은 테이블에도 쓸 수 있다. 바꾸는 김에 테이블의 VariableNames도 적당히 바꿔주자.
raceTable(:, 2:end) = cumsum(raceTable(:, 2:end), 2);
varnames = string(raceTable.Properties.VariableNames(2:end));
varnames = extract(varnames, "~" + digitsPattern);
raceTable.Properties.VariableNames(2:end) = varnames;

이제 timetable을 만들 준비가 다 되었다. timetable을 만드는 방법은 원 글의 Step 2: 데이터 정리를 참고하였다.
Times = 1:width(raceTable)-1;
names = string(raceTable.Username);
data = table2array(raceTable(:, 2:end));
timeStamp = datetime(Times, 1, 1);
timeStamp.Format = "y'k'";
T = array2timetable(data', 'RowTimes', timeStamp);
T.Properties.VariableNames = names;

이제 정말 끝났다. 소스 코드를 받아서 돌리면 끝!
barChartRace(T, NumDisplay=10, NumInterp=3, ...
Method="spline", ...
Position=[545 446 962 420], ...
XlabelName='누적 채팅 건수 (2023년 1월 4일~)');

아 참, 소스 코드의 barChartRace.m에 사소한 오류가 하나 있다. 225번 줄을 보면 아래처럼 되어 있다.
%% From the 2nd step and later
for ii=2:length(ranking2plot)
ranking = ranking2plot(ii,:);
value2plot = data2plot(ii,:);
딱 봐도 여기서 애니메이션이 돌아감을 알 수 있는데, length를 쓰면 row 개수가 잡힐지 column 개수가 잡힐지 알 수 없다. 위 코드에서 ranking2plot은 row 개수가 애니메이션의 스텝 수, column 개수가 카테고리 개수(단톡방의 경우 username 수)이다. 위와 같이 쓴 것은 카테고리 개수보다 애니메이션 스텝 수가 더 많을 것이라 가정한 건데, 단톡방은 워낙 많은 사람이 들락날락하여(...) 닉네임 수가 훨씬 많다. 그래서 아래처럼 바꿔 주어야 에러가 발생하지 않는다.
%% From the 2nd step and later
for ii=2:size(ranking2plot, 1)
ranking = ranking2plot(ii,:);
value2plot = data2plot(ii,:);
이 정도만 해도 볼만하지만, 해볼만한 작업들이 있다.
1. 지금은 일정한 크기로 챗을 묶어서 카운드 한다. 좀 복잡해지겠지만 날짜별로 카운트를 할 수도 있다. 단톡방의 대화목록을 내보내면 아래와 같은 형태로 날짜의 시작이 표시된다.
--------------- 2022년 7월 5일 화요일 ---------------
2. 제외하고 싶은 username을 지정할 수 있으면 좋겠다. 방장봇이 얼마나 열심히 일했는지는 궁금하지 않으니까.
추가 작업은 별도의 글로 올리겠다.
끗.
'matlab' 카테고리의 다른 글
| 매트랩에서의 클로저 (0) | 2025.09.19 |
|---|---|
| 단톡방 Bar Chart Race - 날짜별로 끊기 (0) | 2025.09.07 |
| (펌) MATLAB vs PYTHON (0) | 2025.07.08 |
| 코랩에서 매트랩 돌리기 (0) | 2025.06.29 |
| 40585 = 4! + 0! + 5! + 8! + 5! (0) | 2025.03.16 |