From 9762c61a8d2a2c3bed656ca6899d5d4f656ac503 Mon Sep 17 00:00:00 2001 From: nwickel Date: Fri, 22 Mar 2024 15:58:30 +0100 Subject: [PATCH] Updated README.Rmd and exported as github_document --- README.Rmd | 539 ++++++++---------------- README.md | 577 ++++++++++++++++++++++++++ README_files/figure-gfm/timems-1.png | Bin 0 -> 6367 bytes README_files/figure-gfm/xycoord-1.png | Bin 0 -> 12056 bytes 4 files changed, 745 insertions(+), 371 deletions(-) create mode 100644 README.md create mode 100644 README_files/figure-gfm/timems-1.png create mode 100644 README_files/figure-gfm/xycoord-1.png diff --git a/README.Rmd b/README.Rmd index 8bbca70..3b2c9e6 100644 --- a/README.Rmd +++ b/README.Rmd @@ -1,46 +1,38 @@ --- -title: "Background information about MTT data" -author: "Nora Wickelmaier" -date: "`r Sys.Date()`" -output: - html_document: - number_sections: true - toc: true +title: "Log data from the Multi-Touch Table at the HAUM" +output: github_document --- ```{r, include = FALSE} -# setwd("C:/Users/nwickelmaier/Nextcloud/Documents/MDS/2023ss/60100_master_thesis") -devtools::load_all("../../../software/mtt") +devtools::load_all("../../../../software/mtt") ``` -# Log data from the Multi-Touch Table at the HAUM - The Multi Touch Table at the Herzog-Anton-Ulrich-Museum (HAUM) in Braunschweig gives visitors of the Museum the opportunity to interact with -67 artworks and 3 tiles containing information about the museum and its -layout. The table was installed at the institute in October 2016 and since -November 2016 log files from interactions of visitors of the museum have -been collected. These log files are in an unstructured format and cannot be -easily analyzed. The purpose of the following document is to describe how -the data haven been transformed and which decisions have been made along -the way. +about 70 artworks and 3 virtual cards containing information about the +museum and its layout. The table was installed at the institute in October +2016 and since November 2016 log files from interactions of visitors of the +museum have been collected. These log files are in an unstructured format +and cannot be easily analyzed. The purpose of the following document is to +describe how the data haven been transformed and which decisions have been +made along the way. # Data structure The log files contain lines that indicate the beginning and end of possible -actions that can be performed when interacting with the artworks on the -table. The layout of the table looks like 70 pictures have been tossed on a +activities that can be performed when interacting with the artworks on the +table. The layout of the table looks like pictures have been tossed on a large table. Every artwork is visible at the start configuration. People can move the pictures on the table, they can be scaled and rotated. Additionally, the virtual picture cards can be flipped in order to find more information of the artwork on the "back" of the card. One has to press a little `i` for more information in one of the bottom corners of the card. -On the back of the card two (?) to six information cards can be found with -a teaser text about a certain topic. These topic cards can be opened and a -hypertext with detailed information pops up. Within these hypertexts -certain technical terms can be clicked for lay people to get more -information. This also opens up a pop-up. The events encoded in the raw log -files therefore have the following structure. +On the back of the card two to six information cards can be found with a +teaser text about a certain topic. These topic cards can be opened and a +hypertext with detailed information opens. Within these hypertexts certain +technical terms can be clicked for lay people to get more information. This +also opens up a pop-up. The events encoded in the raw log files therefore +have the following structure. ``` "Start Application" --> Start Application @@ -100,32 +92,32 @@ raw log file: organized in. For the HAUM data set, the data are sorted by year (folders 2016, 2017, 2018, 2019, 2020, 2021, 2022, and 2023). -* `data`: Extracted time stamp from the raw log file in the format +* `date`: Extracted timestamp from the raw log file in the format `yyyy-mm-dd hh:mm:ss`. -* `timeMs`: Containing a time stamp in Milliseconds that restarts with +* `timeMs`: Containing a timestamp in Milliseconds that restarts with every new raw log files. * `event`: Start and stop event tags. See above for possible values. -* `artwork`: Identifier of the different artworks. This is a 3 digit - (left-padded) number. The numbers of the artworks correspond to the +* `item`: Identifier of the different items. This is a three-digit + (left-padded) number. The numbers of the items correspond to the folder names in `/ContentEyevisit/eyevisit_cards_light/` and were orginally taken from the museums catalogue. -* `popup`: Name of the pop-up opened. This is only interestin for +* `popup`: Name of the pop-up opened. This is only interesting for "openPopup" events. -* `topicNumber`: The number of the topic card that has been opened at the back of - the artwork card. See below for a more detailed descripttion what these - numbers possibly mean. +* `topic`: The number of the topic card that has been opened at the back of + the item card. See below for a more detailed descripttion what these + numbers mean. * `x`: Value of x-coordinate in pixel on the 4K-Display ($3840 \times 2160$) * `y`: Value of y-coordinate in pixel -* `scale`: Number in 128 bit that indicates how much the artwork card has - been scaled (????) +* `scale`: Number in 128 bit that indicates how much the card has been + scaled * `rotation`: Degree of rotation in start configuration. @@ -134,43 +126,45 @@ raw log file: ## Variables after "closing of events" -The raw log data consists of start and stop events for each event type. -After preprocessing for event types are extracted: `move`, `flipCard`, +The raw log data consist of start and stop events for each event type. +After preprocessing four event types are extracted: `move`, `flipCard`, `openTopic`, and `openPopup`. Except for the `move` events, which can occur -at any time when interacting with an artwork card on the table, the events -have a hierachical order: An artwork card first needs to be flipped +at any time when interacting with an item card on the table, the events +have a hierarchical order: An item card first needs to be flipped (`flipCard`), then the topic cards on the back of the card can be opened (`openTopic`), and finally pop-ups on these topic cards can be opened (`openPopup`). This implies that the event `openPopup` can only be present -for a certain artwork, if the card has already been flipped (i.e., an event -`flipCard` for the same artwork has already occured). +for a certain item, if the card has already been flipped (i.e., an event +`flipCard` for the same item has already occured). After preprocessing, the data frame is now in a wide format with columns for the start and the stop of each event and contains the following variables: -* `folder`: Containing the folder name (see above) +* `fileId.start` / `fileId.stop`: See above. -* `eventId`: A numerical variable that indicates the number of the event. - Starts at 1 and ends with the total number of events, counting up by 1. +* `date.start` / `date.stop`: See above. + +* `folder`: Containing the folder name (see above) * `case`: A numerical variable indicating cases in the data. A "case" indicates an interaction interval and could be defined in different ways. - Right now a new case begins, when no event occured for 20 seconds. + Right now a new case begins, when no event occurred for 20 seconds or + longer. -* `trace`: A trace is defined as one interaction with one artwork. A trace - can either start with a `flipCard` event or when an artwork has been - touched for the first time within this case. A trace ends with the - artwork card being flipped close again or with the last movement of the - card within this case. One case can contain several traces with the same - artwork when the artwork is flipped open and slipped close again several +* `path`: A path is defined as one interaction with one item A path + can either start with a `flipCard` event or when an item has been + touched for the first time within this case. A path ends with the + item card being flipped close again or with the last movement of the + card within this case. One case can contain several paths with the same + item when the item is flipped open and flipped close again several times within a short time. * `glossar`: An indicator variable with values 0/1 that tracks if a pop-up has been opened from the glossar folder. These pop-ups can be assigned to - the wronge artwork since it is not possible to do this algorithmically. - It is possible that two artworks are flipped open that could both link to - the same popup from a glossar. The indicator variable is left as a + the wrong item since it is not possible to do this algorithmically. + It is possible that two items are flipped open that could both link to + the same pop-up from a glossar. The indicator variable is left as a variable, so that these pop-ups can be easily deleted from the data. Right now, glossar entries can be ignored completely by setting an argument and this is done by default. Using the pop-ups from the glossar @@ -179,20 +173,16 @@ variables: * `event`: Indicating the event. Can take tha values `move`, `flipCard`, `openTopic`, and `openPopup`. -* `artwork`: Identifier of the different artworks. This is a 3 digit - (left-padded) number. See above. - -* `fileId.start` / `fileId.stop`: See above. - -* `date.start` / `date.stop`: See above. +* `item`: Identifier of the different artworks and information cards. This + is a three-digit (left-padded) number. See above. * `timeMs.start` / `timeMs.stop`: See above. * `duration`: Calculated by $timeMs.stop - timeMs.start$ in Milliseconds. Needs to be adjusted for events spanning more than one log file by a - factor of $60,000 \times #logfiles$. See below for details. + factor of $60,000 \times \text{number of logfiles}$. See below for details. -* `topicNumber`: See above. +* `topic`: See above. * `popup`: See above. @@ -200,11 +190,12 @@ variables: * `y.start` / `y.stop`: See above. -* `distance`: Euclidean distande calculated from $(x.start, y.start)$ and $(x.stop, y.stop)$. +* `distance`: Euclidean distande calculated from $(x.start, y.start)$ and + $(x.stop, y.stop)$. * `scale.start` / `scale.stop`: See above. -* `scaleSize`: Relative scaling of artwork card, calculated by +* `scaleSize`: Relative scaling of item card, calculated by $\frac{scale.stop}{scale.start}$. * `rotation.start` / `rotation.stop`: See above. @@ -215,60 +206,26 @@ variables: ## How unclosed events are handled Events do not necessarily need to be completed. A person can, e.g., leave -the table and not flip the artwork card close again. For `flipCard`, +the table and not flip the item card close again. For `flipCard`, `openTopic`, and `openPopup` the data frame contains `NA` when the event -does not complete. For `move` events is happens quite often that a start +does not complete. For `move` events it happens quite often that a start event follows a start event and a stop event follows a stop event. Technically a move event cannot *not* be finished and the number of events -without a start or stop indicated that the time resolution was not +without a start or stop indicate that the time resolution was not sufficient to catch all these events accurately. Double start and stop -`move`events have therefore been deleted from the data set. - - +`move` events have therefore been deleted from the data set. ## Additional meta data For the HAUM data, I added meta data on state holidays and school -vacations. Additionally, the topic categories of the topic cards were -extracted from the XML files and added to the data frame. +vacations. This led to the following additional variables: -* `topicIndex` - -* `topicFile` - -* `topic` - -* `state` (Niedersachsen for complete HAUM data set) - -* `stateCode` (NI) - * `holiday` * `vacations` -* `stateCodeVacations` - - - # Problems and how I handled them This lists some problems with the log data that required decisions. These @@ -287,33 +244,12 @@ event spans more than two log files, a multiple of $600,000$ must be taken, e.g. for three log files it must be: $2 \times 600,000 - timeMs.start + timeMs.stop$ and so on. -```{r, results = FALSE, fig.show = TRUE} +```{r timems, echo = FALSE, results = FALSE, fig.show = TRUE} # Read data -dat0 <- read.table("data/haum/raw_logfiles_small_2023-09-26_13-50-20.csv", sep = ";", - header = TRUE) -dat0$date <- as.POSIXct(dat0$date) -dat0$glossar <- ifelse(dat0$artwork == "glossar", 1, 0) +datraw <- read.table("code/results/raw_logfiles_2024-02-21_16-07-33.csv", sep = ";", + header = TRUE) -# Remove irrelevant events -dat <- subset(dat0, !(dat0$event %in% c("Start Application", - "Show Application"))) - -# Add trace variable -artworks <- unique(stats::na.omit(dat$artwork)) -artworks <- artworks[artworks != "glossar"] -glossar_files <- unique(subset(dat, dat$artwork == "glossar")$popup) -glossar_dict <- create_glossardict(artworks, glossar_files, - xmlpath = "data/haum/ContentEyevisit/eyevisit_cards_light/") -dat1 <- add_trace(dat, glossar_dict) - -# Close events -dat2 <- rbind(close_events(dat1, "move", rm_nochange_moves = TRUE), - close_events(dat1, "flipCard", rm_nochange_moves = TRUE), - close_events(dat1, "openTopic", rm_nochange_moves = TRUE), - close_events(dat1, "openPopup", rm_nochange_moves = TRUE)) -dat2 <- dat2[order(dat2$fileId.start, dat2$date.start, dat2$timeMs.start), ] - -plot(timeMs ~ as.factor(fileId), dat[1:5000,], xlab = "fileId") +plot(timeMs ~ as.factor(fileId), datraw[1:5000,], xlab = "fileId") ``` The boxplot shows that we have a continuous range of values within one log @@ -322,7 +258,7 @@ file but that `timeMs` does not increase over log files. I kept in the data frame, so it is clear when events span more than one log file. log error! -``` - -I did not check all of these cases (for the complete data set this is -simply not possible by hand) but just excluded all events that do not have -a `date.start` since they are hard to interpret. Often they are log errors -but in some cases they might be resolvable. - -```{r} -# remove all events that do not have a `date.start` -dim(dat2[is.na(dat2$date.start), ]) -dat2 <- dat2[!is.na(dat2$date.start), ] -``` - -In order to deal with these logging errors, I check the data for what I -call "fragmented traces". These are traces that cannot happen, when -everything is logged correctly, e.g., traces containing `flipCard -> -openPopup` or traces that only consist of `move`, `openTopic`, and -`openPopup` events. These fragmented traces are removed from the data. It -was not possible to check them all manually, but the 20 or more that I do -check in the raw log files were all some kind of logging error like above. -Most often a card was already closed again, before a topic card or pop-up -was recorded as being closed. - ## Card indices go from 0 to 7 (instead of 0 to 5 as expected) -See `questions_number-of-cards.R` for more details. +In the beginning I thought that the number for topics was the index of +where the card was presented on the back of the item. But this is not +correct. It is the number of the topic. There are eight topics in total: -I wrote a function that for each artwork extracts the file names of the -possible topic cards and then looks up which topics have actually been -displayed on the back of the card. I added an index giving the ordering in -the index files. - -The possible values in the variable `topicNumber` range from 0 to 7, -however, no artwork has more than six different numbers. So I just renamed -those numbers from 1 to the highest number, e.g., $0,1,2,4,5,6$ was changed -to $0\to 1,1\to 2,2\to 3,4\to 4,5\to 5,6\to 6$. Next I used the index to -assign topics and file names to the according pop-ups. This needs to be -cross checked with the programming, but seems the most plausible approach -with my current knowledge. - - - -## Extracting topics from `index.xml` vs. `.xml` - -When I extract the topics from `index.html` I get different topics, than -when I get them from `.html`. At first glance, it looks like using -`index.html` actually gives the wrong results. - -```{r} -artworks <- unique(dat2$artwork) -path <- "data/haum/ContentEyevisit/eyevisit_cards_light/" -topics <- extract_topics(artworks, rep("index.xml", length(artworks)), path) -topics2 <- extract_topics(artworks, paste0(artworks, ".xml"), path) - -topics[!topics$file_name %in% topics2$file_name, ] -topics2[!topics2$file_name %in% topics$file_name, ] ``` +Indices for topics: +0 artist +1 thema +2 komposition +3 leben des kunstwerks +4 details +5 licht und farbe +6 extra info +7 technik +``` +On the back of items, there can be between 2 to 6 topic cards. Several of +these topic cards can be about the same topic, e.g., there can be two topic +cards assigned to the topic `thema`. It is impossible to find out if the +same topic card was opened several times or if different topic cards with +the same topic were opened from the same item. See example below for item +"001". -For artwork "031", `index.html` only defines 5 cards (the 6th is commented -out), but `topicNumber` for this artwork has 6 different entries. I will -therefore extract the topics from `.html`. (This seems also better -compatible with other data sets like 8o8m.) +```{r topics, echo = FALSE} +items <- sprintf("%03d", unique(datlogs$item)) +topics <- extract_topics(items, xmlfiles = paste0(items, ".xml"), + xmlpath = "data/haum/ContentEyevisit/eyevisit_cards_light/") +head(topics) +``` ## New artworks "504" and "505" starting October 2022 When I read in the complete data frame for the first time, all of the -sudden there were 72 instead of 70 artworks. It seems like these two +sudden there were 72 instead of 70 items. It seems like these two artworks appear on October 21, 2022. -```{r} -dat0 <- read.table("data/haum/raw_logfiles_2023-09-23_01-31-30.csv", - sep = ";", header = TRUE) -dat0$date <- as.POSIXct(dat0$date) -dat0$glossar <- ifelse(dat0$artwork == "glossar", 1, 0) - -# Remove irrelevant events -dat <- subset(dat0, !(dat0$event %in% c("Start Application", - "Show Application"))) - -summary(dat[dat$artwork %in% c("504", "505"), ]) +```{r newitems} +summary(as.Date(datraw[datraw$item %in% c("504", "505"), "date"])) ``` -The artworks seem to be have updated in general after October 21, 2022. +The artworks seem to be have updated in general after October 21, 2022. The +following table shows which items were presented in which years. -```{r} -art_after_oct2022 <- sort(unique(dat[dat$date >= "2022-10-21", "artwork"])) -art_before_oct2022 <- sort(unique(dat[dat$date <= "2022-10-21", "artwork"])) -# Removed artworks -art_before_oct2022[!art_before_oct2022 %in% art_after_oct2022] -# Additional artworks -art_after_oct2022[!art_after_oct2022 %in% art_before_oct2022] +```{r years} +xtabs(~ item + lubridate::year(date.start), datlogs) ``` -The following table shows which artworks were presented in which years. - -```{r} -xtabs(~ artwork + lubridate::year(date), dat) -``` - -It strongly suggests that the artworks haven been updated after the Corona -pandemic. I think, the table was also moved to a different location at that -point. (Check with PG to make sure.) - -# Optimizing resources used by the code - -After I started trying out the functions on the complete data set, it -became obvious (not surprisingly `:)`) that this will not work -- -especially for the move events. The reshape function cannot take a long -data frame with over 6 Million entries and convert it into a wide data -frame (at least not on my laptop). The code is supposed to work "out of the -box" for researchers, hence it *should* run on a regular (8 core) laptop. -So, I changed the reshaping so that it is done in batches on subsets of the -data for every `fileId` separately. This means that events that span over -two (or more) raw log files cannot be closed and will then be removed from -the data set. The function warns about this, but it is a random process -getting rid of these data and seems therefore not like a systematic -problem. Another reason why this is not bad, is that durations cannot be -calculated for events across log files anyways, because the time stamps do -not increase systematically over log files (see above). - -UPDATE: By now, I close the events spanning more than one log file after -this has been done. - -I meant to put the lists back together with `do.call(rbind, some_list)` but -this can also not handle big data sets. I therefore switched to -`dplyr::bind_rows(some_ist)` which is really fast and was developed -especially for this purpose. It means, that I have to depend on the dplyr -package (which I am not a big fan of, since I meant to keep the package -self-contained). - -# Reading list - -* @Arizmendi2022 [--] -* @Bannert2014 [x] -* @Bousbia2010 [--] -* @Cerezo2020 -* @GerjetsSchwan2021 [x] -* @Goldhammer2020 -* @Guenther2007 -* @HuberBannert2023 [x] -* @Kroehne2018 -* @SchwanGerjets2021 [x] -* @vanderAalst2016 [Chap. 2, x] -* @vanderAalst2016 [Chap. 3] -* @vanderAalst2016 [Chap. 5, x] -* @Wang2019 - -# Open stuff - -* Angle from which people approach table in Braunschweig? Consider in - rotation variable? -* Time limit for `case` variable different for different events? (openTopic - should be opened the longest) - - $\to$ I think this is not relevant since I am looking at time *between* - events! - -# Stuff AK found interesting - -* Pre/post corona -* Identify school classes -* How many persons are present at the table? - -# Other potential questions - -* "Bursts" -* 1st vs. 2nd half of the day -* Can we identify "types of art"? With clustering or something? -* Possible to estimate how many persons per day? Maybe average of certain - weekdays? ... ? +It shows that the artworks haven been updated after the Corona pandemic. I +think, the table was also moved to a different location at that point. diff --git a/README.md b/README.md new file mode 100644 index 0000000..80ca431 --- /dev/null +++ b/README.md @@ -0,0 +1,577 @@ +Log data from the Multi-Touch Table at the HAUM +================ + +The Multi Touch Table at the Herzog-Anton-Ulrich-Museum (HAUM) in +Braunschweig gives visitors of the Museum the opportunity to interact +with about 70 artworks and 3 virtual cards containing information about +the museum and its layout. The table was installed at the institute in +October 2016 and since November 2016 log files from interactions of +visitors of the museum have been collected. These log files are in an +unstructured format and cannot be easily analyzed. The purpose of the +following document is to describe how the data haven been transformed +and which decisions have been made along the way. + +# Data structure + +The log files contain lines that indicate the beginning and end of +possible activities that can be performed when interacting with the +artworks on the table. The layout of the table looks like pictures have +been tossed on a large table. Every artwork is visible at the start +configuration. People can move the pictures on the table, they can be +scaled and rotated. Additionally, the virtual picture cards can be +flipped in order to find more information of the artwork on the “back” +of the card. One has to press a little `i` for more information in one +of the bottom corners of the card. On the back of the card two to six +information cards can be found with a teaser text about a certain topic. +These topic cards can be opened and a hypertext with detailed +information opens. Within these hypertexts certain technical terms can +be clicked for lay people to get more information. This also opens up a +pop-up. The events encoded in the raw log files therefore have the +following structure. + + "Start Application" --> Start Application + "Show Application" + "Transform start" --> Move + "Transform stop" + "Show Info" --> Flip Card + "Show Front" + "Artwork/OpenCard" --> Open Topic + "Artwork/CloseCard" + "ShowPopup" --> Open Popup + "HidePopup" + +The right side shows what events can be extracted from these raw lines. +The “Start Application” is not an event in the original sense since it +only indicates if the table was started or maybe reset itself. This is +not an interaction with the table and therefore not interesting in +itself. All “Start Application” and “Show Application” are therefore +excluded from the data when further processed and are only in the raw +log files. + +# Parsing the raw log files + +The first step is to parse the raw log files that are stored by the +application as text files in a rather unstructured format to a format +that can be read by common statistics software packages. The data are +therefore transferred to a spread sheet format. The following section +describes what problems were encountered while doing this. + +## Corrupt lines + +When reading the files containing the raw logs into R, a warning appears +that says + + Warning messages: + incomplete final line found on '2016/2016_11_18-11_31_0.log' + incomplete final line found on '2016/2016_11_18-11_38_30.log' + incomplete final line found on '2016/2016_11_18-11_40_36.log' + ... + +When you open these files, it looks like the last line contains some +binary content. It is unclear why and how this happens. So when reading +the data, these lines were removed. A warning will be given that +indicates how many files have been affected. + +## Extracted variables from raw log files + +The following variables (columns in the data frame) are extracted from +the raw log file: + +- `fileId`: Containing the zero-left-padded file name of the raw log + file the data line has been extracted from + +- `folder`: The folder names in which the raw log files haven been + organized in. For the HAUM data set, the data are sorted by year + (folders 2016, 2017, 2018, 2019, 2020, 2021, 2022, and 2023). + +- `date`: Extracted timestamp from the raw log file in the format + `yyyy-mm-dd hh:mm:ss`. + +- `timeMs`: Containing a timestamp in Milliseconds that restarts with + every new raw log files. + +- `event`: Start and stop event tags. See above for possible values. + +- `item`: Identifier of the different items. This is a three-digit + (left-padded) number. The numbers of the items correspond to the + folder names in `/ContentEyevisit/eyevisit_cards_light/` and were + orginally taken from the museums catalogue. + +- `popup`: Name of the pop-up opened. This is only interesting for + “openPopup” events. + +- `topic`: The number of the topic card that has been opened at the back + of the item card. See below for a more detailed descripttion what + these numbers mean. + +- `x`: Value of x-coordinate in pixel on the 4K-Display + ($3840 \times 2160$) + +- `y`: Value of y-coordinate in pixel + +- `scale`: Number in 128 bit that indicates how much the card has been + scaled + +- `rotation`: Degree of rotation in start configuration. + + + +## Variables after “closing of events” + +The raw log data consist of start and stop events for each event type. +After preprocessing four event types are extracted: `move`, `flipCard`, +`openTopic`, and `openPopup`. Except for the `move` events, which can +occur at any time when interacting with an item card on the table, the +events have a hierarchical order: An item card first needs to be flipped +(`flipCard`), then the topic cards on the back of the card can be opened +(`openTopic`), and finally pop-ups on these topic cards can be opened +(`openPopup`). This implies that the event `openPopup` can only be +present for a certain item, if the card has already been flipped (i.e., +an event `flipCard` for the same item has already occured). + +After preprocessing, the data frame is now in a wide format with columns +for the start and the stop of each event and contains the following +variables: + +- `fileId.start` / `fileId.stop`: See above. + +- `date.start` / `date.stop`: See above. + +- `folder`: Containing the folder name (see above) + +- `case`: A numerical variable indicating cases in the data. A “case” + indicates an interaction interval and could be defined in different + ways. Right now a new case begins, when no event occurred for 20 + seconds or longer. + +- `path`: A path is defined as one interaction with one item A path can + either start with a `flipCard` event or when an item has been touched + for the first time within this case. A path ends with the item card + being flipped close again or with the last movement of the card within + this case. One case can contain several paths with the same item when + the item is flipped open and flipped close again several times within + a short time. + +- `glossar`: An indicator variable with values 0/1 that tracks if a + pop-up has been opened from the glossar folder. These pop-ups can be + assigned to the wrong item since it is not possible to do this + algorithmically. It is possible that two items are flipped open that + could both link to the same pop-up from a glossar. The indicator + variable is left as a variable, so that these pop-ups can be easily + deleted from the data. Right now, glossar entries can be ignored + completely by setting an argument and this is done by default. Using + the pop-ups from the glossar will need a lot more love, before it + behaves satisfactorily. + +- `event`: Indicating the event. Can take tha values `move`, `flipCard`, + `openTopic`, and `openPopup`. + +- `item`: Identifier of the different artworks and information cards. + This is a three-digit (left-padded) number. See above. + +- `timeMs.start` / `timeMs.stop`: See above. + +- `duration`: Calculated by $timeMs.stop - timeMs.start$ in + Milliseconds. Needs to be adjusted for events spanning more than one + log file by a factor of $60,000 \times \text{number of logfiles}$. See + below for details. + +- `topic`: See above. + +- `popup`: See above. + +- `x.start` / `x.stop`: See above. + +- `y.start` / `y.stop`: See above. + +- `distance`: Euclidean distande calculated from $(x.start, y.start)$ + and $(x.stop, y.stop)$. + +- `scale.start` / `scale.stop`: See above. + +- `scaleSize`: Relative scaling of item card, calculated by + $\frac{scale.stop}{scale.start}$. + +- `rotation.start` / `rotation.stop`: See above. + +- `rotationDegree`: Difference of rotation from $rotation.stop$ to + $rotation.start$. + +## How unclosed events are handled + +Events do not necessarily need to be completed. A person can, e.g., +leave the table and not flip the item card close again. For `flipCard`, +`openTopic`, and `openPopup` the data frame contains `NA` when the event +does not complete. For `move` events it happens quite often that a start +event follows a start event and a stop event follows a stop event. +Technically a move event cannot *not* be finished and the number of +events without a start or stop indicate that the time resolution was not +sufficient to catch all these events accurately. Double start and stop +`move` events have therefore been deleted from the data set. + +## Additional meta data + +For the HAUM data, I added meta data on state holidays and school +vacations. + +This led to the following additional variables: + +- `holiday` + +- `vacations` + +# Problems and how I handled them + +This lists some problems with the log data that required decisions. +These decisions influence the outcome and maybe even the data quality. +Hence, I tried to document how I handled these problems and explain the +decisions I made. + +## Weird behavior of `timeMs` and neg. `duration` values + +`timeMs` resets itself every time a new log file starts. This means that +the durations of events spanning more than one log file must be +adjusted. Instead of just calculating $timeMs.stop - timeMs.start$, +`timeMs.start` must be subtracted from the maximum duration of the log +file where the event started ($600,000 ms$) and the `timeMs.stop` must +be added. If the event spans more than two log files, a multiple of +$600,000$ must be taken, e.g. for three log files it must be: +$2 \times 600,000 - timeMs.start + timeMs.stop$ and so on. + +![](README_files/figure-gfm/timems-1.png) + +The boxplot shows that we have a continuous range of values within one +log file but that `timeMs` does not increase over log files. I kept +`timeMs.start` and `timeMs.stop` and also `fileId.start` and +`fileId.stop` in the data frame, so it is clear when events span more +than one log file. + + + +## Left padding of file IDs + +The file names of the raw log files are automatically generated and +contain a timestamp. This timestamp is not well formed. First, it +contains an incorrect month. The months go from 0 to 11 which means, +that the file name `2016_11_15-12_12_57.log` was collected on December +15, 2016 at 12:12 pm. Another problem is that the file names are not +zero left padded, e.g., `2016_11_15-12_2_57.log`. This file was +collected on December 15, 2016 at 12:02 pm and therefore before the file +above. But most sorting algorithms, will sort these files in the order +shown below. In order to preprocess the data and close events that +belong together, the data need to be sorted by events and artworks +repeatedly. In order to get them back in the correct time order, it is +necessary to order them based on three variables: `fileId.start`, +`date.start` and `timeMs.start`. The file IDs therefore need to sort in +the correct order (again see below for example). I zero left padded the +log file names within the data frame using it as an identifier. These +“file names” do not correspond exactly to the original raw log file +names. This needs to be kept in mind when doing any kind of matching +etc. + + ## what it looked like before left padding + # 1422 ../data/haum_logs_2016-2023/_2016b/2016_11_15-12_2_57.log 2016-12-15 12:12:56 599671 Transform start 076 076.xml NA 2092.25 2008.00 0.3000000 13.26874254 + # 1423 ../data/haum_logs_2016-2023/_2016b/2016_11_15-12_12_57.log 2016-12-15 12:12:57 621 Transform start 076 076.xml NA 2092.25 2008.00 0.3000000 13.26523465 + # 1424 ../data/haum_logs_2016-2023/_2016b/2016_11_15-12_12_57.log 2016-12-15 12:12:57 677 Transform stop 076 076.xml NA 2092.25 2008.00 0.2997736 13.26239605 + # 1425 ../data/haum_logs_2016-2023/_2016b/2016_11_15-12_12_57.log 2016-12-15 12:12:57 774 Transform start 076 076.xml NA 2092.25 2008.00 0.2999345 13.26239605 + # 1426 ../data/haum_logs_2016-2023/_2016b/2016_11_15-12_12_57.log 2016-12-15 12:12:57 850 Transform stop 076 076.xml NA 2092.25 2008.00 0.2997107 13.26223362 + # 1427 ../data/haum_logs_2016-2023/_2016b/2016_11_15-12_2_57.log 2016-12-15 12:12:57 599916 Transform stop 076 076.xml NA 2092.25 2008.00 0.2997771 13.26523465 + + ## what it looks like now + # 1422 2016_11_15-12_02_57.log 2016-12-15 12:12:56 599671 Transform start 076 076.xml NA 2092.25 2008.00 0.3000000 13.26874254 + # 1423 2016_11_15-12_02_57.log 2016-12-15 12:12:57 599916 Transform stop 076 076.xml NA 2092.25 2008.00 0.2997771 13.26523465 + # 1424 2016_11_15-12_12_57.log 2016-12-15 12:12:57 621 Transform start 076 076.xml NA 2092.25 2008.00 0.3000000 13.26523465 + # 1425 2016_11_15-12_12_57.log 2016-12-15 12:12:57 677 Transform stop 076 076.xml NA 2092.25 2008.00 0.2997736 13.26239605 + # 1426 2016_11_15-12_12_57.log 2016-12-15 12:12:57 774 Transform start 076 076.xml NA 2092.25 2008.00 0.2999345 13.26239605 + # 1427 2016_11_15-12_12_57.log 2016-12-15 12:12:57 850 Transform stop 076 076.xml NA 2092.25 2008.00 0.2997107 13.26223362 + +## Timestamps repeat + +The timestamps in the `date` variable record year, month, day, hour, +minute and seconds. Since one second is not a very short time interval +for a move on a touch display, this is not fine grained enough to bring +events into the correct order, meaning there are events from the same +log file having the same timestamp and even events from different log +files having the same timestamp. The log files get written about every +10 minutes (which can easily be seen when looking at the file names of +the raw log files). So in order to get events in the correct order, it +is necessary to first order by file ID, within file ID then sort by +timestamp `date` and then within these more coarse grained timestamps +sort be `timeMs`. But as explained above, `timeMs` can only be sorted +within one file ID, since they do not increase consistently over log +files, but have a new setoff for each raw log file. + +## x,y-coordinates outside of display range + +The display of the Multi-Touch-Table is a 4K-display with 3840 x 2160 +pixels. When you plot the start and stop coordinates, the display is +clearly distinguishable. However, a lot of points are outside of the +display range. This can happen, when the art objects are scaled and then +moved to the very edge of the table. Then it will record pixels outside +of the table. These are actually valid data points and I will leave them +as is. + +``` r +datlogs <- read.table("code/results/event_logfiles_2024-02-21_16-07-33.csv", sep = ";", + header = TRUE) + +par(mfrow = c(1, 2)) +plot(y.start ~ x.start, datlogs) +abline(v = c(0, 3840), h = c(0, 2160), col = "blue", lwd = 2) +plot(y.stop ~ x.stop, datlogs) +abline(v = c(0, 3840), h = c(0, 2160), col = "blue", lwd = 2) +``` + +![](README_files/figure-gfm/xycoord-1.png) + +``` r +aggregate(cbind(x.start, x.stop, y.start, y.stop) ~ 1, datlogs, mean) +``` + + ## x.start x.stop y.start y.stop + ## 1 1978.202 1975.876 1137.481 1133.494 + +## Pop-ups from glossar cannot be assigned to a specific item + +All the information, pictures and texts for the topics and pop-ups are +stored in +`/data/haum/ContentEyevisit/eyevisit_cards_light/`. Among +other things, each folder contains XML-files with the information about +any technical terms that can be opened from the hypertexts on the topic +cards. Often these information are item dependent and then the +corresponding XML-file is in the folder for this item. Sometimes, +however, more general terms can be opened. In order to avoid multiple +files containing the same information, these were stored in a folder +called `glossar` and get accessed from there. The raw log files only +contain the path to this glossar entry and did not record from which +item it was accessed. I tried to assign these glossar entries to the +correct items. The (very heuristic) approach was this: + +1. Create a lookup table with all XML-file names (possible pop-ups) + from the glossar folder and what items possibly call them. This was + stored as an `RData` object for easier handling but should maybe be + stored in a more interoperable format. + +2. I went through all possible pop-ups in this lookup table and stored + the items that are associated with it. + +3. I created a sub data frame without move events (since they can never + be associated with a pop-up) and went through every line and looked + up if an item and a topic card had been opened. If this was the case + and a glossar entry came up before the item was closed again, I + assigned this item to the glossar entry. + +This is heuristic since it is possible that several topic cards from +different items are opened simultaneously and the glossar pop-up could +be opened from either one (it could even be more than two, of course). +In these cases the item that was opened closest to the glossar pop-up +has been assigned, but this can never be completely error free. + +And this heuristic only assigns a little more than half of the glossar +entries. Since my heuristic only looks for the last item that has been +opened and if this item is a possible candidate it misses all glossar +pop-ups where another item has been opened in between. This is still an +open TODO to write a more elaborate algorithm. + +All glossar pop-ups that do not get matched with an item are removed +from the data set with a warning if the argument `glossar = TRUE` is +set. Otherwise the glossar entries will be ignored completely. + +## Assign a `case` variable based on “time heuristic” + +One thing needed in order to work with the data set and use it for +machine learning algorithms like process mining, is a variable that +tries to identify a case. A case variable will structure the data frame +in a way that navigation behavior can actually be investigated. However, +we do not know if several people are standing around the table +interacting with it or just one very active person. The simplest way to +define a case variable is to just use a time limit between events. This +means that when the table has not been interacted with for, e.g., 20 +seconds than it is assumed that a person moved on and a new person +started interacting with the table. This is the easiest heuristic and +implemented at the moment. Process mining shows that this simple +approach works in a way that the correct process gets extracted by the +algorithm. + +In order to investigate user behavior on a more fine grained level, it +will be necessary to come up with a more elaborate approach. A better, +still simple approach, could be to use this kind of time limit and +additionally look at the distance between items interacted with within +one time window. When items are far apart it seems plausible that more +than one person interacted with them. Very short time lapses between +events on different items could also be an indicator that more than one +person is interacting with the table. + +## Assign a `path` variable + +The `path` variable is supposed to show one interaction trace with one +artwork. Meaning it starts when an artwork is touched or flipped and +stops when it is closed again. It is easy to assign a path from flipping +a card over opening (maybe several) topics and pop-ups for this artwork +card until closing this card again. But one would like to assign the +same path to move events surrounding this interaction. Again, this is +not possible in an algorithmic way but only heuristically. + +Again, I used a time cutoff for this. First, if a `move` event occurs, +it is checked, if the same item has been flipped less than 20 seconds +beforehand. If yes, the same path indicator is assigned to this `move`. +If not, temporarily a new “move indicator” is assigned. Then, a +“backward pass” is applied, where it is checked if the same item is +opened less than 20 seconds *after* the event occurs. If yes, that path +indicator is assigned. For all the remaining moves, a new path number is +assigned. This corresponds to items being moved without being flipped. + +## A `move` event does not record any change + +Most of the events in the log files are move events. Additionally, many +of these move events are recorded but they do not indicate any change, +meaning the only difference is the timestamp. All other variables +indicating moves like `x.start` and `x.stop`, `rotation.start` and +`rotation.stop` etc. do not show *any* change. They represent about 2/3 +of all move events. These events are probably short touches of the table +without an actual interaction. They were therefore removed from the data +set. + +## Card indices go from 0 to 7 (instead of 0 to 5 as expected) + +In the beginning I thought that the number for topics was the index of +where the card was presented on the back of the item. But this is not +correct. It is the number of the topic. There are eight topics in total: + + Indices for topics: + 0 artist + 1 thema + 2 komposition + 3 leben des kunstwerks + 4 details + 5 licht und farbe + 6 extra info + 7 technik + +On the back of items, there can be between 2 to 6 topic cards. Several +of these topic cards can be about the same topic, e.g., there can be two +topic cards assigned to the topic `thema`. It is impossible to find out +if the same topic card was opened several times or if different topic +cards with the same topic were opened from the same item. See example +below for item “001”. + + ## item file_name topic + ## 1 001 001_dargestellte.xml thema + ## 2 001 001_thema1.xml thema + ## 3 001 001_leben.xml leben des kunstwerks + ## 4 001 001_leben3.xml leben des kunstwerks + ## 5 001 001_thema2.xml thema + ## 6 001 001_thema.xml thema + +## New artworks “504” and “505” starting October 2022 + +When I read in the complete data frame for the first time, all of the +sudden there were 72 instead of 70 items. It seems like these two +artworks appear on October 21, 2022. + +``` r +summary(as.Date(datraw[datraw$item %in% c("504", "505"), "date"])) +``` + + ## Min. 1st Qu. Median Mean 3rd Qu. Max. + ## "2022-10-21" "2023-01-11" "2023-03-08" "2023-03-09" "2023-05-21" "2023-07-05" + +The artworks seem to be have updated in general after October 21, 2022. +The following table shows which items were presented in which years. + +``` r +xtabs(~ item + lubridate::year(date.start), datlogs) +``` + + ## lubridate::year(date.start) + ## item 2016 2017 2018 2019 2020 2022 2023 + ## 1 277 4082 1912 1434 424 394 1315 + ## 3 485 6730 3126 2356 528 457 1124 + ## 19 714 8656 4028 2743 660 698 1595 + ## 20 595 8461 3996 2983 938 657 1355 + ## 24 497 6638 2912 2251 649 439 1028 + ## 27 567 5959 3112 2318 651 711 1324 + ## 28 601 9329 4394 3056 778 762 1570 + ## 29 425 6865 3830 2365 516 615 1174 + ## 31 289 4118 2051 1218 291 296 675 + ## 32 562 7016 3477 2253 726 766 1647 + ## 33 509 4936 2242 1449 555 358 666 + ## 36 434 4505 2276 1668 373 387 976 + ## 37 242 4478 2182 1554 339 423 1168 + ## 38 480 4617 2144 1397 371 381 784 + ## 39 395 3227 1313 1003 237 161 622 + ## 41 282 3329 1303 1022 225 209 701 + ## 42 203 3113 1307 903 242 191 421 + ## 43 115 2420 1089 806 176 219 486 + ## 45 1491 13561 5924 4474 966 585 1828 + ## 46 903 9181 5340 3812 961 944 1648 + ## 47 306 4949 2395 1510 750 297 675 + ## 48 723 10455 5384 4162 1328 948 2031 + ## 49 433 4326 2124 1414 434 431 809 + ## 51 564 7837 4577 2991 884 659 1370 + ## 52 447 5021 2104 1729 471 349 840 + ## 54 424 5068 2816 2008 529 370 918 + ## 55 358 4859 2069 1428 341 403 1303 + ## 57 860 14264 6625 5092 1410 1221 2714 + ## 60 555 6865 3539 2336 639 586 1415 + ## 62 547 6736 3803 2210 795 633 1322 + ## 63 251 3677 1827 1241 300 282 527 + ## 66 552 6004 2774 1977 505 373 932 + ## 69 394 3730 1827 1438 272 206 680 + ## 70 226 3766 1843 973 293 268 703 + ## 71 557 6160 2490 1846 570 323 839 + ## 72 426 6194 2857 2129 508 635 1553 + ## 73 432 6125 2880 1821 583 395 939 + ## 75 258 5885 2418 1562 369 257 645 + ## 76 861 12435 6253 4214 1753 1153 2268 + ## 77 816 8595 4197 2897 699 674 1452 + ## 78 410 5632 2498 1924 394 408 850 + ## 80 1650 25687 12429 7782 1975 1712 4433 + ## 83 644 8618 4720 3026 987 1027 2294 + ## 84 184 2121 1231 759 231 254 465 + ## 87 149 1618 722 632 99 0 0 + ## 88 513 6996 3493 2272 539 533 1420 + ## 89 214 2204 950 723 156 0 0 + ## 90 281 3756 1372 1143 403 320 932 + ## 93 613 8528 4224 3015 696 1174 2058 + ## 98 462 6662 3265 2565 704 670 1453 + ## 99 180 4162 1653 1454 363 411 868 + ## 101 414 4209 1859 1282 392 411 981 + ## 103 677 8758 4366 3165 1045 909 1871 + ## 104 423 5256 2381 1865 463 467 933 + ## 107 181 2101 1106 788 205 146 339 + ## 109 321 4001 1619 1106 292 188 453 + ## 110 489 5846 2785 2008 494 387 923 + ## 125 640 8435 4519 3334 926 0 0 + ## 129 598 11322 5046 3369 910 1131 1682 + ## 145 419 7821 3945 2694 706 740 1396 + ## 176 507 8465 3968 2787 687 552 1544 + ## 180 516 7563 3720 2765 585 550 1272 + ## 183 377 4014 1819 1741 346 251 675 + ## 187 340 4222 2165 1753 319 312 734 + ## 197 426 7710 3603 2510 671 602 1217 + ## 229 303 4872 2360 1891 482 389 1005 + ## 231 271 3606 1851 1239 318 236 467 + ## 501 1915 15968 7849 5060 1157 890 2989 + ## 502 1212 14550 7111 4749 1105 883 2752 + ## 503 1308 15218 8632 6399 1626 870 2558 + ## 504 0 0 0 0 0 363 662 + ## 505 0 0 0 0 0 426 1533 + +It shows that the artworks haven been updated after the Corona pandemic. +I think, the table was also moved to a different location at that point. diff --git a/README_files/figure-gfm/timems-1.png b/README_files/figure-gfm/timems-1.png new file mode 100644 index 0000000000000000000000000000000000000000..f08b70a63d0c950cd4623691eaf8d27af425ac64 GIT binary patch literal 6367 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 W-PG|DT#ZM6RG1iA7(6?79sCc^BlETZ literal 0 HcmV?d00001 diff --git a/README_files/figure-gfm/xycoord-1.png b/README_files/figure-gfm/xycoord-1.png new file mode 100644 index 0000000000000000000000000000000000000000..d72a279bfabf216cb56408c781fe85f7408d10de GIT binary patch literal 12056 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