☹️ 지금까지 무슨 삶을 살아오신 거죠?
Sleepwalking은 지난 2년 간의 수면 데이터와 걷기 데이터로 바라본 제 삶 그 자체입니다.
대학생, 교환학생, 인턴을 거쳐 밤새는 병약한 대학생이 되기까지, 그 환경에 따라 변화하는 저의 생활 방식을 재미있게 살펴볼 수 있도록 기획했습니다.
Sleepwalking은 지난 2년 간의 수면 데이터와 걷기 데이터로 바라본 제 삶 그 자체입니다.
대학생, 교환학생, 인턴을 거쳐 밤새는 병약한 대학생이 되기까지, 그 환경에 따라 변화하는 저의 생활 방식을 재미있게 살펴볼 수 있도록 기획했습니다.
Sleepwalking is my life itself, as I've seen with sleep data and walking data over the past two years.
From being a college student, an exchange student, and an intern to being a sick college student all night, I planned to have fun looking at my changing lif estyle according to the environment.
🤔 The part without any concern of getting data
2년간의 수면 데이터로 본 내 생활
아이폰에는 수면 시간을 기록해주는 기능이 있음
2년동안 수면 시간을 확인해본 결과, 재미있는 특성들을 발견함
이것들을 재미있게 시각화해서 수면 시간으로 살펴 본 내 생활 보고서를 만들면 어떨까?
음악으로 음악가의 게놈 지도를 만들면 어떨까?
이걸론 괜찮은 결과물이 절대 못나오겠군요☹☹☹
단톡방은 이야기꽃밭(?)
사람들은 메신저에 많은 단톡방을 가지고 있음
단톡방마다 그 성격도 다르고, 오가는 얘기도 다르고 주제도 다르다.
그럼에도 모두 이런저런 이야기가 오간다는 점에서 그 공통점이 있다
'이야기꽃을 피운다'라는 말에서 착안해, 내 카카오톡 단톡방을 분석해,각 단톡방을 꽃으로 표현해보면 어떨까라는 아이디어
☹️ Painful, endless iteration of data refining and idea sketch
주의 데이터 정제 결과이지, 프로젝트 초안이 아닙니다!
Warning This is visualization of processed data, not the
draft for project!
낮에는 걷고, 밤에는 자는 기록..
# data-energy.csv
2021-10-09 0:00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,98,620,600,0,194,440,341,0,0,0,0
2021-10-10 0:00,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,630,0,0,0,0,0,0,0,551,945,0,572,0,0,0,0,0,0,0,0
2021-10-11 0:00,651,0,0,0,0,0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,759,723,0,0,0,0,258,0,64,0,804,0,801,0,0,0,0,0,0,0,0
2021-10-12 0:00,0,0,2278,5,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,60,60,0,0,0,0,0,9,0,0,15,1357,1174,306,1562,0,0,22,0,0,0,0,0,0
2021-10-13 0:00,2407,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,291,0,0,0,0,0,0,117,2548,834,0,1358,0,0,0,0,0,19,0,0
2021-10-14 0:00,0,0,1686,665,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,246,1233,961,0,0,2234,2187,0,0,0,0,0,0,0
2021-10-15 0:00,0,0,0,0,2233,2900,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,0,0,0,288,0,8,1616,776,0,393,452,675,528,0,0,0,0,0,0,8,0,0
2021-10-16 0:00,0,979,1334,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,242,332,463,2931,364,0,0,0,0,0,491,1470,0,0,0,0,455,1881,42,42,0
2021-10-17 0:00,0,0,0,1939,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1961,64,16,938,385,244,550,0,0,0,0,0,0,0,0
...
// sketch.js
function preload() {
data = loadTable("result-energy.csv", "csv");
}
function setup() {
createCanvas(600, 800);
noLoop();
}
function draw() {
//this makes sure there is content in the data
if (data) {
//get the amount of rows in the CSV
let numRows = data.getRowCount();
let numCols = data.getColumnCount();
print(numRows, numCols);
for (let d = 0; d < numRows; d++) {
for (let t = 1; t < numCols; t++) {
// visualize the energy
let energy = data.getNum(d, t);
let length = map(energy, 0, 3000, 0, 30);
// Change this random value for more randomness
let offset = random(-4, 4);
// Circle version
// noStroke();
// fill(231, 95, 65, 255 * 0.5);
// circle(25 + 10 * t, 50 + 40 * Math.floor(d / 7) + offset, length);
// Square version
fill(231, 95, 65, 255 * 0.2);
stroke(231, 95, 65, 255 * 0.8);
let x = 25 + 10 * t;
let y = 50 + 40 * Math.floor(d / 7) + offset - length / 2;
square(x, y, length);
}
}
}
}
My initial sketch has two ideas of integrating static data(sleeping) with dynamic data(walking).
In this assignment, I tried to implement dynamic data using extracted csv data.
I choose the red-orange color with transparency.
Since walking implies - daytime, sun, active, blood(?), fire - these color hue will suit.
In addition, I'm planning to use blue-navy color for static data.
Circular shape and square shape was introduced due to consistency between static data.
For circular shape, it will be placed after normal (or sine) wave which represents static data.
Since both are consisted of soft curves, it will fit well.
For square shape, it will be placed after sequence of square with
different color
which represents static data.
Since dynamic data is represented as size of square instead of color,
comparing
difference between change in color and change in size would be
effective.
To deliver image of being dynamic more effectively,
I introduced way
to 'scatter' y-axis value of each square.
This would make each
element fell as if they are alive and more dynamic.
So, let's start that “engineers' stuff”☹
다 때려부시고 처음부터 데이터 예쁘게 수집하기 프로젝트🥲
이전에 1장에서 XML 형식의 Apple 건강 데이터를 성공적으로 추출하는 데 성공했으나, 데이터에 일부 문제점이 있었습니다.
이를 해결하기 위해 코드 수정 및 데이터를 추출하는 과정을 단순화했고, 그 결과는 다음과 같습니다.
before = [[0, 0, 0, ... , 0], [0, 0, 0, ... , 0], ... , [0, 0, 0, ... , 0]]
after = [0, 0, 0, 0, 0, ... 0, 0]
# data-region.csv
2022-07-11 00:00:00 +0900,Asia/Seoul
2023-08-27 09:15:41 +0900,Asia/Riyadh
2023-08-27 16:40:58 +0900,America/New_York
2023-11-22 20:12:10 +0900,America/Toronto
2023-11-26 23:16:18 +0900,America/New_York
2023-12-24 22:13:39 +0900,Asia/Seoul
2024-07-01 10:34:17 +0900,Asia/Dubai
2024-07-03 02:10:24 +0900,Europe/Rome
2024-07-05 17:05:53 +0900,Europe/Paris
2024-07-20 10:09:08 +0900,Asia/Seoul
2024-10-23 12:26:58 +0900,Australia/Sydney
2024-10-29 16:32:12 +0900,Asia/Seoul
데이터를 정제해서 모으는 전체 프로세스는 다음과 같습니다.
HKCategoryTypeIdentifierSleepAnalysis 태그를 가진 요소를
모두 취합
data-sleep.csv로 저장
data-sleep.csv의 각 열을 읽어, 시작 시간부터 종료
시간까지 해당하는 블록에 값 저장.→ 30분 단위의 블록 각각에 대해 해당
수면 시간이 차지하는 만큼의 시간(분) 값을 더함.→ (예) 11시 20분부터
12시 40분까지인 경우, 각 블록은 10, 30, 30, 10으로
채워짐.
result-sleep.txt로 저장
# result-sleep.txt
0
0
30
30
...
30
result-sleep.csv로 저장
# result-sleep.csv
2022-07-11,0,0,0,0,0,0,30,30,30,30,30,30,30,30,30,30,30,30,30,30,0,0,0,...
2022-07-12,0,0,0,0,0,0,0,11,30,30,30,30,30,30,30,30,30,30,30,30,28,0,0,0,...
2022-07-13,0,0,0,0,0,0,0,0,0,10,30,30,30,30,30,30,30,30,30,30,0,0,0,0,0,0,...
2022-07-14,0,0,0,0,0,0,0,24,30,30,30,30,30,30,30,30,30,30,30,30,9,0,0,0,0,...
HKQuantityTypeIdentifierStepCount 태그를 가진 요소를 모두
취합
data-region.csv를 이용하여 지역 정보를 가져옴
data-walk.csv에 저장data-walk.csv의 각 열을 읽어, 시작 시간부터 종료 시간까지
해당하는 블록에 값 저장. 10, 10, 10, 10으로 채워짐.
result-walk.txt로 저장
# result-walk.txt
559
823
...
8
605
1745
result-walk.csv로 저장
# result-walk.csv
2022-07-11,0,180,479,290,0,8,0,559,823,1494,344,694,303,8,605,1745,763,...
2022-07-12,1943,0,1366,0,0,947,457,0,0,0,0,72,438,153,0,107,527,996,1628,...
2022-07-13,0,1563,0,502,905,0,1346,382,0,0,1184,0,0,0,93,92,0,0,95,1336,...
2022-07-14,0,0,597,92,0,0,46,0,0,1100,701,447,2548,1077,262,0,120,0,0,0,0,...
result-sleep.txt와 result-walk.txt로부터
수면 데이터 리스트를 생성
sleep_list = [0, 0, 0, 0, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, ...]
walk_list = [0, 0, 0, 0, 0, 180, 479, 290, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...]
(x, y)는 해당 블록동안 x분
수면, y 걸음을 걸었음을 의미.
sleepwalk_list[0] = [(0, 0), (0, 180), (0, 479), (0, 290), (0, 0), ...]
sleepwalk_list[1] = [(0, 410), (0, 410), (0, 0), (0, 0), (0, 1943), ...]
sleepwalk_list[2] = [(0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 1563), ...]
sleepwalk_list[3] = [(0, 1410), (0, 771), (0, 0), (0, 0), (0, 0), ...]
sleepwalk_list[4] = [(0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), ...]
result-sleepwalk.json의 형태로 저장.
// result-sleepwalk.json
{
"2022-07-11": [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 180], ...],
"2022-07-12": [[0, 410], [0, 410], [0, 0], [0, 0], [0, 1943], ...],
"2022-07-13": [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 1563], ...],
"2022-07-14": [[0, 1410], [0, 771], [0, 0], [0, 0], [0, 0], [0, 0], ...],
...
}
혹시 다른 분들께 도움이 되진 않을까 싶어, 작성한 코드를 공유합니다.
참고사항
소스 코드
f-sleep.py# %% [markdown]
# Let's collect sleep data from xml data!
# %%
import xml.etree.ElementTree as ET
import csv
tree = ET.parse("../export.xml")
root = tree.getroot()
with open("data-sleep.csv", "w", encoding="utf-8-sig", newline="") as fp:
writer = csv.writer(fp)
for child in root:
if (
child.tag == "Record"
and child.attrib["type"] == "HKCategoryTypeIdentifierSleepAnalysis"
):
start, end = child.attrib["startDate"], child.attrib["endDate"]
location = child[0].attrib["value"]
# Filter date
if start < "2022-07-11 00:00:00 +0900":
continue
row = [start, end, location]
print(row)
writer.writerow(row)
# %% [markdown]
# Convert extracted csv into flatten list.
# %%
from datetime import datetime
from zoneinfo import ZoneInfo
import csv
TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
# Initialize time
# Data is collected from 2022-07-11.
init_time = datetime(2022, 7, 11)
curr_time = datetime.today()
# Initialize sleep list
sleep_list = [0 for _ in range((curr_time - init_time).days)] * 48
with open("data-sleep.csv", "r", encoding="utf-8-sig") as fp:
reader = csv.reader(fp)
for start, end, region in reader:
start_time = datetime.strptime(start[:-6], TIME_FORMAT)
end_time = datetime.strptime(end[:-6], TIME_FORMAT)
# Get timezone info
try:
tz_curr = ZoneInfo(region)
except:
continue
start_time = start_time.astimezone(tz_curr).replace(tzinfo=None)
end_time = end_time.astimezone(tz_curr).replace(tzinfo=None)
start_delta = start_time - init_time
end_delta = end_time - init_time
start_index = (start_delta.days * 48) + (start_delta.seconds // 1800)
end_index = (end_delta.days * 48) + (end_delta.seconds // 1800)
# Skip counting if time is before time_init
if start_index < 0:
continue
start_value = (1800 - (start_delta.seconds % 1800)) // 60
end_value = (end_delta.seconds % 1800) // 60
for index in range(start_index, end_index + 1):
if index == start_index:
sleep_list[index] = start_value
elif index == end_index:
sleep_list[index] = end_value
else:
sleep_list[index] = 30
print(sleep_list)
# %% [markdown]
# Export sleep list into text
# %%
with open("result-sleep.txt", "w", encoding="utf-8") as fp:
for sleep in sleep_list:
fp.write(f"{sleep}\n")
# %% [markdown]
# Export sleep list into csv (deprecated maybe...)
# %%
from datetime import datetime, timedelta
init_time = datetime(2022, 7, 11)
with open("result-sleep.csv", "w", encoding="utf-8-sig", newline="") as fp:
writer = csv.writer(fp)
for day in range(len(sleep_list) // 48):
index = day * 48
date = datetime.strftime(init_time + timedelta(day), "%Y-%m-%d")
row = [date, *sleep_list[index : index + 48]]
writer.writerow(row)
f-walk.py# %% [markdown]
# There is no timezone data in walk data.
# Let's get data from hand-made csv file!
# %%
import csv
def get_region(date: str):
with open("data-region.csv", "r", encoding="utf-8-sig") as fp:
reader = csv.reader(fp)
result = ""
for bound, region in reader:
if date >= bound:
result = region
else:
break
return result
# %% [markdown]
# Let's collect sleep data from xml data!
# %%
import xml.etree.ElementTree as ET
import csv
tree = ET.parse("../export.xml")
root = tree.getroot()
with open("data-walk.csv", "w", encoding="utf-8-sig", newline="") as fp:
writer = csv.writer(fp)
for child in root:
if (
child.tag == "Record"
and child.attrib["type"] == "HKQuantityTypeIdentifierStepCount"
):
start = child.attrib["startDate"]
end = child.attrib["endDate"]
steps = child.attrib["value"]
region = get_region(start)
# Filter date
if start < "2022-07-11 00:00:00 +0900":
continue
row = [start, end, steps, region]
print(row)
writer.writerow(row)
# %% [markdown]
# Convert extracted csv into flatten list.
# %%
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
import csv
TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
# Initialize time and result list
init_time = datetime(2022, 7, 11)
curr_time = datetime.today()
# Initialize walk list
walk_list = [0 for _ in range((curr_time - init_time).days)] * 48
with open("data-walk.csv", "r", encoding="utf-8-sig") as fp:
reader = csv.reader(fp)
for start, end, steps, region in reader:
start_time = datetime.strptime(start[:-6], TIME_FORMAT)
end_time = datetime.strptime(end[:-6], TIME_FORMAT)
# Get timezone info
try:
tz_curr = ZoneInfo(region)
except:
continue
start_time = start_time.astimezone(tz_curr).replace(tzinfo=None)
end_time = end_time.astimezone(tz_curr).replace(tzinfo=None)
start_delta = start_time - init_time
end_delta = end_time - init_time
start_index = (start_delta.days * 48) + (start_delta.seconds // 1800)
end_index = (end_delta.days * 48) + (end_delta.seconds // 1800)
start_value = (1800 - (start_delta.seconds % 1800)) // 60
end_value = (end_delta.seconds % 1800) // 60
steps = int(steps) // (end_index - start_index + 1)
for index in range(start_index, end_index + 1):
walk_list[index] += steps
print(walk_list)
# %% [markdown]
# Export sleep list into text
# %%
with open("result-walk.txt", "w", encoding="utf-8") as fp:
for walk in walk_list:
fp.write(f"{walk}\n")
# %% [markdown]
# Export sleep list into csv (deprecated maybe...)
# %%
from datetime import datetime, timedelta
with open("result-walk.csv", "w", encoding="utf-8-sig", newline="") as fp:
writer = csv.writer(fp)
for day in range(len(walk_list) // 48):
index = day * 48
date = datetime.strftime(init_time + timedelta(day), "%Y-%m-%d")
row = [date, *walk_list[index : index + 48]]
writer.writerow(row)
f-merge.py# %% [markdown]
# ### Merge sleep data with walking data
#
# Ah, engineering thing!
# %% [markdown]
# Collect sleep and walk data from text result.
# %%
# Start data from 22:00
sleep_list = [0, 0, 0, 0]
walk_list = [0, 0, 0, 0]
with open("result-sleep.txt", "r", encoding="utf-8") as fp:
sleep_list += [int(line) for line in fp.readlines()]
with open("result-walk.txt", "r", encoding="utf-8") as fp:
walk_list += [int(line) for line in fp.readlines()]
print(sleep_list)
print(len(sleep_list))
print(walk_list)
print(len(walk_list))
# %% [markdown]
# Zip two data into tuple and apply dynamic daylength.
#
# Q. Is making element as tuple is really a good idea?
# %%
sleepwalk_data = list(zip(sleep_list, walk_list))
sleepwalk_list = []
for day in range(len(sleepwalk_data) // 48):
index = day * 48
buffer = sleepwalk_data[index : index + 48]
# Append data until the sleeping time occurs.
# It is guaranteed that `index + 48` is not a sleeping time.
for sleep, walk in sleepwalk_data[index + 48 : index + 96]:
if sleep > 0:
break
# Add data to buffer
buffer.append((sleep, walk))
# Add buffer to result list
sleepwalk_list.append(buffer)
_ = [print(sleepwalk) for sleepwalk in sleepwalk_list]
# %% [markdown]
# Export sleep list into json.
# %%
from datetime import datetime, timedelta
import json
# Set initial time
init_time = datetime(2022, 7, 11)
# Convert sleepwalk list into json object
json_obj = {}
for day, sleepwalk in enumerate(sleepwalk_list):
date = datetime.strftime(init_time + timedelta(day), "%Y-%m-%d")
json_obj[date] = [[sleep, walk] for sleep, walk in sleepwalk]
# Print result log
for k, v in json_obj.items():
print(k, v)
# Save json object as json file
with open("result-merge.json", "w") as fp:
json.dump(json_obj, fp)
이히히히히히 된다 된다 된다 히히히🤪
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.1/addons/p5.sound.min.js"></script>
<script src="https://unpkg.com/p5.js-svg@1.5.0"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<main>
</main>
<script src="sketch.js"></script>
</body>
</html>
// sketch.js
function preload() {
dataJSON = loadJSON("result-merge.json");
}
function setup() {
createCanvas(1600, 5300, SVG);
noLoop();
}
function draw() {
//this makes sure there is content in the data
if (dataJSON) {
let data = dataJSON.data;
print(data.length)
for (let i = 0; i < data.length; i++) {
let datum = data[i];
let date = datum.date;
let sleepwalk = datum.sleepwalk;
// Add label
if (i % 7 == 0) {
fill(0, 0, 0);
noStroke();
text(date, 10, 40 * Math.floor(i / 7) + 55);
}
for (let j = 0; j < sleepwalk.length; j++) {
// Unpack sleep and walk value
let sleep = sleepwalk[j][0];
let walk = sleepwalk[j][1];
// Visualize sleep
let sleep_size = 5;
let sleep_alpha = map(sleep, 0, 30, 0, 255 * 0.14)
fill(121, 166, 205, sleep_alpha);
stroke(121, 166, 205, sleep_alpha * 4);
let x_sleep = 80 + 10 * j;
let y_sleep = 50 + 40 * Math.floor(i / 7) - sleep_size / 2;
square(x_sleep, y_sleep, sleep_size);
// Visualize walk
let walk_size = map(walk, 0, 3000, 0, 30);
let walk_offset = random(-4, 4);
if (walk_size) {
fill(231, 95, 65, 255 * 0.14);
stroke(231, 95, 65, 255 * 0.56);
} else {
noFill();
noStroke();
}
let x_walk = 80 + 10 * j;
let y_walk = 50 + 40 * Math.floor(i / 7) + walk_offset - walk_size / 2;
square(x_walk, y_walk, walk_size);
}
}
}
}
Merge it! Remove it! Simplify it!
It will make your SVG much smaller but make your code much bigger☹☹
6, 7장에서 일단 성공적으로 데이터를 시각화하는 데는 성공했습니다.
하지만 여전히 할 일은 많았는데, 만들어진 SVG 데이터가 너무 커서 일러스트레이터에서 사용하기에 너무 불편하다는 문제가 있었습니다.
무려 16MB를 넘는 육중한 사이즈에 컴퓨터는 울부짖고 방은 히터를 안 때도 따뜻합니다.
이를 해결하기 위해 일부 최적화를 진행했습니다.
255 / 6으로 설정한
블록을 겹쳐 표현했다면,이제는 주 단위로 데이터를 모두 합쳐 블록 당 최대
210분을 기준으로 [0, 255]의 범위로 맵핑했습니다.
위의 피나는(?) 노력 덕분에, 16MB가 넘던 SVG 데이터는 이제 3.6MB 수준으로
감소했습니다.
컴퓨터도 이제는 덜 화난 것 같군요!
sketch.js// sketch.js
function preload() {
dataJSON = loadJSON("assets/result-merge.json");
font = loadFont("assets/Pretendard-Bold.otf");
}
function setup() {
createCanvas(1600, 5300, SVG); // Create canvas
noStroke(); // We don't use stroke
noLoop(); // Iterate only once
}
function draw() {
//this makes sure there is content in the data
if (dataJSON) {
let data = dataJSON.data;
let weekly_sleep = new Array(96).fill(0);
for (let i = 0; i < data.length; i++) {
let datum = data[i];
let date = datum.date;
let sleepwalk = datum.sleepwalk;
// Work to do for the first weekday
if (i % 7 == 0) {
// Add title for row
fill(0, 0, 0);
textSize(10);
textFont(font);
text(date, 10, 40 * Math.floor(i / 7) + 55);
// Reset weekly sleep array
weekly_sleep = new Array(96).fill(0);
}
// Iterate blocks in daily sleepwalk data
for (let j = 0; j < sleepwalk.length; j++) {
// Unpack sleep and walk value
let sleep = sleepwalk[j][0];
let walk = sleepwalk[j][1];
// Append sleep data into weekly sleep
weekly_sleep[j] += sleep;
// Calculate pivot coordinate
let x = 80 + 10 * j;
let y = 50 + 40 * Math.floor(i / 7);
// Visualize walk
let walk_size = map(walk, 0, 3000, 0, 30);
let walk_offset = random(-4, 4);
if (walk_size) {
let x_walk = x - walk_size / 2;
let y_walk = y + walk_offset - walk_size / 2;
fill(220, 70, 20, 255 * 0.25);
square(x_walk, y_walk, walk_size);
}
}
// Work to do for the last weekday
if (i % 7 == 6) {
// Draw weekly sleep data
for (let j = 0; j < 96; j++) {
// Unpack sleep value from weekly sleep
let sleep = weekly_sleep[j];
// Calculate pivot coordinate
let x = 80 + 10 * j;
let y = 50 + 40 * Math.floor(i / 7);
// Visualize sleep
let sleep_size = 8;
let sleep_alpha = map(sleep, 0, 210, 0, 255)
if (sleep_alpha) {
let x_sleep = x - sleep_size / 2;
let y_sleep = y - sleep_size / 2;
fill(25, 50, 75, sleep_alpha);
square(x_sleep, y_sleep, sleep_size);
}
}
}
}
}
}
디자인 상으로도 약간의 변화를 주었습니다.
최대한 저 시각화를 아름답게, 그리고 사람들이 읽기 편하게 만들어야 해요🤔
구분선 없이 위의 데이터를 바라본다면, 사람들은 단순히 시간의 연속적인 흐름에 따른 변화만을 확인할 수 있을 것입니다.
하지만 구분선을 통해 연속적인 시간의 흐름을 그룹화할 수 있고,각 그룹 사이의 비교를 통해 보다 직관적이고 확실한 비교가 가능해지게 됩니다.
구분선을 어떻게 그어야 할까요?
이전의 데이터는 위아래 여백이 너무 넓어서,
쓸데 없이 낭비되는 공간이 많았습니다.
줄 간격을 줄이고, 대신 너비를 넓혀 줄 오른쪽에 캡션이나 설명을 넣을 수 있도록 합니다.
이제 높이 대 너비 비도 3:1에서 2:1로 줄어들었습니다! 인쇄하기 훨씬 좋겠군요!
sketch.js// sketch.js
function preload() {
dataJSON = loadJSON("assets/result-merge.json");
font = loadFont("assets/Pretendard-Bold.otf");
}
function setup() {
createCanvas(1300, 3900, SVG); // Create canvas
noStroke(); // We don't use stroke
noLoop(); // Iterate only once
}
function draw() {
//this makes sure there is content in the data
if (dataJSON) {
let data = dataJSON.data;
let weekly_sleep = new Array(96).fill(0);
for (let i = 0; i < 630; i++) { // 7 days * 90 weeks
let datum = data[i];
let date = datum.date;
let sleepwalk = datum.sleepwalk;
// Work to do for the first weekday
if (i % 7 == 0) {
// Add title for row
fill(0, 0, 0);
textSize(10);
textFont(font);
text(date, 50, 205 + 30 * Math.floor(i / 7));
// Reset weekly sleep array
weekly_sleep = new Array(96).fill(0);
}
// Iterate blocks in daily sleepwalk data
for (let j = 0; j < sleepwalk.length; j++) {
// Unpack sleep and walk value
let sleep = sleepwalk[j][0];
let walk = sleepwalk[j][1];
// Append sleep data into weekly sleep
weekly_sleep[j] += sleep;
// Calculate pivot coordinate
let x = 120 + 15 * j;
let y = 200 + 30 * Math.floor(i / 7);
// Visualize walk
let walk_size = map(walk, 0, 3000, 0, 30);
let walk_offset = random(-4, 4);
if (walk_size) {
let x_walk = x - walk_size / 2;
let y_walk = y + walk_offset - walk_size / 2;
fill(220, 70, 20, 255 * 0.25);
square(x_walk, y_walk, walk_size);
}
}
// Work to do for the last weekday
if (i % 7 == 6) {
// Draw weekly sleep data
for (let j = 0; j < 96; j++) {
// Unpack sleep value from weekly sleep
let sleep = weekly_sleep[j];
// Calculate pivot coordinate
let x = 120 + 15 * j;
let y = 200 + 30 * Math.floor(i / 7);
// Visualize sleep
let sleep_size = 12;
let sleep_alpha = map(sleep, 0, 210, 0, 255)
if (sleep_alpha) {
let x_sleep = x - sleep_size / 2;
let y_sleep = y - sleep_size / 2;
fill(25, 50, 75, sleep_alpha);
square(x_sleep, y_sleep, sleep_size);
}
}
}
}
}
}
Find the best color for project 🌈
초안의 이상한(?) 색깔을 멀쩡하게 바꾸려는 시도입니다.
다음과 같은 사항이 고려되었습니다.
수많은 레퍼런스 탐색과 끝없는 색깔 섞기를 반복한 끝에, 사용할 색을
정했습니다.
애착 형성(???)을 위해 색깔 별로 이름도 붙여 주었습니다.
Midnight Navy #06121F
차가운 도시의 밤 색깔
배경 색깔로 활용
Moon Yellow #F5E162
밝게 빛나는 달의 색깔
수면 데이터 색깔로 활용
Moonstone Jade #6BC8C3
구름에 비치는 달빛 색깔
걷기 데이터 색깔로 활용
sleep-walking의 데이터는 아이폰이 사용자의 화면 사용 여부에 따라 사용자의 수면 패턴을 기록하는 기능이 있기에 모아질 수 있었습니다.
빌어먹을 애플은 iOS18 업데이트 이후로 이 기능을 삭제했고, 수면 데이터 수집을 위해서는 반드시 애플워치를 등록하여 차고 자야 하게 되었습니다.
저도 애플 사용자긴 하지만, 저는 애플이 참 밉습니다. Sleepwalking은 강제로 치료당했고, 이제는 walking만 남게 되었습니다.
업데이트는 9월 경이었는데, 제가 과제하느라 죽어가면서 업데이트를 질질 미뤄서정말 다행히도 11월 말까지 데이터가 모일 수 있었습니다.
애플이 제 프로젝트를 망칠 뻔했군요..