티스토리 뷰

 

 

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

 

그런데 해당 글을 다시 보니 문제가 있음을 깨달았다. 오리지날 코드는 데이터가 아래와 같이 timetable 형태여야 한다.

 

https://matlabtutorial.github.io/bar_chart_race2.html (원문: https://qiita.com/eigs/items/c1675e6dc6fd497e714a)

 

위 데이터는 일본의 각 현의 인구수 변화이다. 첫 번째 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)

 

당당하게 1등 먹음

 

 

위 코드를 돌리면 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
댓글