├── tests ├── testthat.R ├── spelling.R └── testthat │ ├── DataFromSpotify │ ├── SearchQueriesTest.json │ ├── StreamingHistoryTest.json │ └── PlaylistTest.json │ ├── test-compare_history_test.R │ ├── test-reading_functions.R │ ├── test-cleaning_functions.R │ ├── test-formatting_functions.R │ ├── test-getting_info_functions.R │ └── test-preparing_functions.R ├── .Rbuildignore ├── data ├── clean_playlist.rda ├── clean_stream_his.rda ├── dirty_stream_his.rda ├── clean_stream_his2.rda └── clean_search_queries.rda ├── .gitignore ├── .travis.yml ├── inst └── WORDLIST ├── R ├── dirty_stream_his.R ├── clean_stream_his.R ├── clean_stream_his2.R ├── clean_playlist.R ├── clean_search_queries.R ├── complete_streaming_history.R ├── make_streaming_history_dt.R ├── sessions_length.R ├── make_search_queries_dt.R ├── filter_search_queries.R ├── theme_spotifyviz.R ├── session_for_view.R ├── filter_streaming_history.R ├── longest_session.R ├── make_session_stats.R ├── plot_searches.R ├── sessions_intervals.R ├── prepare_playlists.R ├── str_hist_with_playlists_long.R ├── str_hist_with_playlists_wide.R ├── most_skipped.R ├── plot_day_comparison.R ├── compare_history.R ├── utility_functions.R ├── most_played.R ├── prepare_streaming_history.R ├── count_skipped.R ├── sessions_visualize.R ├── make_two_users_dt.R ├── make_summary_dt.R ├── continuous_listening.R ├── measure_how_long_listened.R ├── plot_track_count.R ├── plot_playlist_popularity.R ├── plot_in_playlists_count.R └── plot_track_count_by_period.R ├── man ├── capitalize.Rd ├── dirty_stream_his.Rd ├── from_sec_to_hms_single.Rd ├── clean_stream_his.Rd ├── clean_stream_his2.Rd ├── make_session_stats.Rd ├── prepare_playlists.Rd ├── plot_searches.Rd ├── from_sec_to_hms.Rd ├── clean_playlist.Rd ├── theme_spotifyviz.Rd ├── sessions_length.Rd ├── make_search_queries_dt.Rd ├── longest_session.Rd ├── sessions_intervals.Rd ├── sessions_visualize.Rd ├── make_streaming_history_dt.Rd ├── plot_day_comparison.Rd ├── clean_search_queries.Rd ├── complete_streaming_history.Rd ├── session_for_view.Rd ├── str_his_with_playlists_long.Rd ├── str_his_with_playlists_wide.Rd ├── compare_history.Rd ├── make_two_users_dt.Rd ├── plot_track_count.Rd ├── continuous_listening.Rd ├── most_played.Rd ├── filter_search_queries.Rd ├── plot_playlist_popularity.Rd ├── make_summary_dt.Rd ├── most_skipped.Rd ├── filter_streaming_history.Rd ├── prepare_streaming_history.Rd ├── count_skipped.Rd ├── plot_in_playlists_count.Rd ├── plot_track_count_by_period.Rd └── measure_how_long_listened.Rd ├── SpotifyViz.Rproj ├── SpotifyViz_n.Rproj ├── DESCRIPTION ├── NAMESPACE ├── README.md └── vignettes └── basic.Rmd /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(spotifyviz) 3 | test_check("spotifyviz") 4 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^doc$ 2 | ^Meta$ 3 | ^.*\.Rproj$ 4 | ^\.Rproj\.user$ 5 | ^\.travis\.yml$ 6 | ^www -------------------------------------------------------------------------------- /data/clean_playlist.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DominikRafacz/SpotifyViz/master/data/clean_playlist.rda -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | .SpotifyViz 6 | doc 7 | Meta 8 | inst/doc 9 | -------------------------------------------------------------------------------- /data/clean_stream_his.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DominikRafacz/SpotifyViz/master/data/clean_stream_his.rda -------------------------------------------------------------------------------- /data/dirty_stream_his.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DominikRafacz/SpotifyViz/master/data/dirty_stream_his.rda -------------------------------------------------------------------------------- /data/clean_stream_his2.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DominikRafacz/SpotifyViz/master/data/clean_stream_his2.rda -------------------------------------------------------------------------------- /data/clean_search_queries.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DominikRafacz/SpotifyViz/master/data/clean_search_queries.rda -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # R for travis: see documentation at https://docs.travis-ci.com/user/languages/r 2 | 3 | language: R 4 | cache: packages 5 | warnings_are_errors: false 6 | -------------------------------------------------------------------------------- /tests/spelling.R: -------------------------------------------------------------------------------- 1 | if (requireNamespace("spelling", quietly = TRUE)) 2 | spelling::spell_check_test(vignettes = TRUE, error = FALSE, 3 | skip_on_cran = TRUE) 4 | -------------------------------------------------------------------------------- /inst/WORDLIST: -------------------------------------------------------------------------------- 1 | artistName 2 | dt 3 | endTime 4 | json 5 | msPlayed 6 | POSIXt 7 | POSTIX 8 | SearchQueries 9 | spotify 10 | Spotify 11 | Spotify's 12 | SpotifyViz 13 | StreamingHistory 14 | trackName 15 | ggplot 16 | listenings 17 | pts 18 | rect 19 | Xh 20 | Ymin 21 | Wiktor's 22 | str -------------------------------------------------------------------------------- /R/dirty_stream_his.R: -------------------------------------------------------------------------------- 1 | #' Dirty streaming history 2 | #' 3 | #' 4 | #' A dataset with dirty streaming history, result of using make_streaming_history_dt() on JSON file, 5 | #' selected from Wiktor's data from spotify, prepared for testing and vignettes. 6 | # 7 | #' @format A data table with 100 rows and 4 variables: 8 | "dirty_stream_his" -------------------------------------------------------------------------------- /R/clean_stream_his.R: -------------------------------------------------------------------------------- 1 | #' Clean streaming history 2 | #' 3 | #' 4 | #' First dataset with prepared streaming history,result of using complete_streaming_history_dt() on JSON file, 5 | #'selected from Wiktor's data from spotify, 6 | #' prepared for testing and vignettes. 7 | # 8 | #' @format A data.table with 100 rows and 7 variables: 9 | "clean_stream_his" -------------------------------------------------------------------------------- /R/clean_stream_his2.R: -------------------------------------------------------------------------------- 1 | #' Clean streaming history 2 | #' 3 | #' 4 | #' Second dataset with prepared streaming history,result of using make_streaming_history_dt() on JSON file, 5 | #' selected from Wiktor's data from spotify, 6 | #' prepared for testing and vignettes. 7 | # 8 | #' @format A data.table with 100 rows and 7 variables: 9 | "clean_stream_his2" -------------------------------------------------------------------------------- /man/capitalize.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utility_functions.R 3 | \name{capitalize} 4 | \alias{capitalize} 5 | \title{Capitalize string} 6 | \usage{ 7 | capitalize(string) 8 | } 9 | \arguments{ 10 | \item{string}{A string that you want to capitalize.} 11 | } 12 | \description{ 13 | Capitalize first letter in the string. 14 | } 15 | -------------------------------------------------------------------------------- /SpotifyViz.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | BuildType: Package 16 | PackageUseDevtools: Yes 17 | PackageInstallArgs: --no-multiarch --with-keep.source 18 | -------------------------------------------------------------------------------- /SpotifyViz_n.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | BuildType: Package 16 | PackageUseDevtools: Yes 17 | PackageInstallArgs: --no-multiarch --with-keep.source 18 | -------------------------------------------------------------------------------- /R/clean_playlist.R: -------------------------------------------------------------------------------- 1 | #' Clean playlist 2 | #' 3 | #' 4 | #' A dataset, selected from Wiktor's data from spotify,result of using prepare_playlists() 5 | #'on JSON file, prepared for testing and vignettes 6 | # 7 | #' @format A data.table with 159 rows and 3 variables: 8 | #' \describe{ 9 | #' \item{track_name}{Track name} 10 | #' \item{artist_name}{Artist name} 11 | #' \item{playlist_name.playlists.name}{Playlist name} 12 | #' } 13 | "clean_playlist" -------------------------------------------------------------------------------- /R/clean_search_queries.R: -------------------------------------------------------------------------------- 1 | #' Clean search queries 2 | #' 3 | #' 4 | #' A dataset with search queries,result of using make_search_queries_dt() on JSON file, 5 | #' selected from Wiktor's data from spotify, prepared for testing and vignettes. 6 | # 7 | #' @format A data table with 10 rows and 3 variables: 8 | #' \describe{ 9 | #' \item{date}{Date, the day the search was performed } 10 | #' \item{platform}{Platform on which the search was performed} 11 | #' \item{country}{Country in which the search was performed} 12 | #' } 13 | "clean_search_queries" -------------------------------------------------------------------------------- /man/dirty_stream_his.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dirty_stream_his.R 3 | \docType{data} 4 | \name{dirty_stream_his} 5 | \alias{dirty_stream_his} 6 | \title{Dirty streaming history} 7 | \format{ 8 | A data table with 100 rows and 4 variables: 9 | } 10 | \usage{ 11 | dirty_stream_his 12 | } 13 | \description{ 14 | A dataset with dirty streaming history, result of using make_streaming_history_dt() on JSON file, 15 | selected from Wiktor's data from spotify, prepared for testing and vignettes. 16 | } 17 | \keyword{datasets} 18 | -------------------------------------------------------------------------------- /man/from_sec_to_hms_single.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utility_functions.R 3 | \name{from_sec_to_hms_single} 4 | \alias{from_sec_to_hms_single} 5 | \title{from_sec_to_hms helper function.} 6 | \usage{ 7 | from_sec_to_hms_single(x) 8 | } 9 | \arguments{ 10 | \item{x}{An integer vector. Number of seconds.} 11 | } 12 | \description{ 13 | Function to be applied to an integer, representing number of seconds, 14 | changes the format to "X h Y min Z s". Applied to each element of the vector by 15 | \code{from_sec_to_hms}. 16 | } 17 | -------------------------------------------------------------------------------- /man/clean_stream_his.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/clean_stream_his.R 3 | \docType{data} 4 | \name{clean_stream_his} 5 | \alias{clean_stream_his} 6 | \title{Clean streaming history} 7 | \format{ 8 | A data.table with 100 rows and 7 variables: 9 | } 10 | \usage{ 11 | clean_stream_his 12 | } 13 | \description{ 14 | First dataset with prepared streaming history,result of using complete_streaming_history_dt() on JSON file, 15 | selected from Wiktor's data from spotify, 16 | prepared for testing and vignettes. 17 | } 18 | \keyword{datasets} 19 | -------------------------------------------------------------------------------- /man/clean_stream_his2.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/clean_stream_his2.R 3 | \docType{data} 4 | \name{clean_stream_his2} 5 | \alias{clean_stream_his2} 6 | \title{Clean streaming history} 7 | \format{ 8 | A data.table with 100 rows and 7 variables: 9 | } 10 | \usage{ 11 | clean_stream_his2 12 | } 13 | \description{ 14 | Second dataset with prepared streaming history,result of using make_streaming_history_dt() on JSON file, 15 | selected from Wiktor's data from spotify, 16 | prepared for testing and vignettes. 17 | } 18 | \keyword{datasets} 19 | -------------------------------------------------------------------------------- /man/make_session_stats.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/make_session_stats.R 3 | \name{make_session_stats} 4 | \alias{make_session_stats} 5 | \title{Make session stats table} 6 | \usage{ 7 | make_session_stats(session_dt) 8 | } 9 | \arguments{ 10 | \item{session_dt}{A data table containing information about listening session. 11 | Must have columns "artist_name", "track_name", "s_played", "start_time".} 12 | } 13 | \description{ 14 | Make a table containing stats about session - play time , number of tracks played, start and end. 15 | } 16 | -------------------------------------------------------------------------------- /man/prepare_playlists.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/prepare_playlists.R 3 | \name{prepare_playlists} 4 | \alias{prepare_playlists} 5 | \title{Prepare playlists data table} 6 | \usage{ 7 | prepare_playlists(folder_path) 8 | } 9 | \arguments{ 10 | \item{folder_path}{Path to a folder which contains file with Playlists in json format. 11 | File must contain "Playlist" in the name.} 12 | } 13 | \value{ 14 | A data table containing the names of playlists and songs on them from spotify. 15 | } 16 | \description{ 17 | Prepare playlists data table 18 | } 19 | -------------------------------------------------------------------------------- /man/plot_searches.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot_searches.R 3 | \name{plot_searches} 4 | \alias{plot_searches} 5 | \title{Visualize search queries} 6 | \usage{ 7 | plot_searches(search_queries, additional = "country") 8 | } 9 | \arguments{ 10 | \item{search_queries}{A data table containing spotify search queries, made with \code{\link{make_search_queries_dt}}} 11 | 12 | \item{additional}{A character vector, additional information to be visualized, can be either "country" or "platform".} 13 | } 14 | \description{ 15 | Visualize search queries 16 | } 17 | -------------------------------------------------------------------------------- /man/from_sec_to_hms.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utility_functions.R 3 | \name{from_sec_to_hms} 4 | \alias{from_sec_to_hms} 5 | \title{Change format to hours minutes seconds} 6 | \usage{ 7 | from_sec_to_hms(x) 8 | } 9 | \arguments{ 10 | \item{x}{An integer vector. Number of seconds.} 11 | } 12 | \value{ 13 | A character vector with format "X h Y min Z s", where Y < 60 and Z < 60 14 | } 15 | \description{ 16 | Change format form number of seconds to number of hours, minutes and seconds. 17 | } 18 | \examples{ 19 | from_sec_to_hms(c(60, 134, 1257)) 20 | 21 | } 22 | -------------------------------------------------------------------------------- /tests/testthat/DataFromSpotify/SearchQueriesTest.json: -------------------------------------------------------------------------------- 1 | [{"date":"2019-09-30","platform":"ANDROID","country":"PL"},{"date":"2019-09-30","platform":"DESKTOP","country":"PL"},{"date":"2019-09-30","platform":"DESKTOP","country":"PL"},{"date":"2019-10-01","platform":"ANDROID","country":"PL"},{"date":"2019-10-01","platform":"ANDROID","country":"PL"},{"date":"2019-10-01","platform":"ANDROID","country":"PL"},{"date":"2019-10-01","platform":"ANDROID","country":"PL"},{"date":"2019-10-01","platform":"ANDROID","country":"PL"},{"date":"2019-10-01","platform":"ANDROID","country":"PL"},{"date":"2019-10-01","platform":"DESKTOP","country":"PL"}] 2 | -------------------------------------------------------------------------------- /man/clean_playlist.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/clean_playlist.R 3 | \docType{data} 4 | \name{clean_playlist} 5 | \alias{clean_playlist} 6 | \title{Clean playlist} 7 | \format{ 8 | A data.table with 159 rows and 3 variables: 9 | \describe{ 10 | \item{track_name}{Track name} 11 | \item{artist_name}{Artist name} 12 | \item{playlist_name.playlists.name}{Playlist name} 13 | } 14 | } 15 | \usage{ 16 | clean_playlist 17 | } 18 | \description{ 19 | A dataset, selected from Wiktor's data from spotify,result of using prepare_playlists() 20 | on JSON file, prepared for testing and vignettes 21 | } 22 | \keyword{datasets} 23 | -------------------------------------------------------------------------------- /man/theme_spotifyviz.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/theme_spotifyviz.R 3 | \name{theme_spotifyviz} 4 | \alias{theme_spotifyviz} 5 | \title{Spotifyviz theme for ggplot2} 6 | \usage{ 7 | theme_spotifyviz( 8 | base_size = 16, 9 | base_family = "", 10 | base_line_size = base_size/22, 11 | base_rect_size = base_size/22 12 | ) 13 | } 14 | \arguments{ 15 | \item{base_size}{Base font size, given in pts.} 16 | 17 | \item{base_family}{Base font family.} 18 | 19 | \item{base_line_size}{Base size for line elements} 20 | 21 | \item{base_rect_size}{Base size for rect elements} 22 | } 23 | \description{ 24 | Theme used in plots. 25 | } 26 | -------------------------------------------------------------------------------- /man/sessions_length.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sessions_length.R 3 | \name{sessions_length} 4 | \alias{sessions_length} 5 | \title{Sessions length} 6 | \usage{ 7 | sessions_length(streaming_history, mins) 8 | } 9 | \arguments{ 10 | \item{streaming_history}{A data.table containing streaming history, after 11 | 'prepare_streaming_history' was used on it.} 12 | 13 | \item{mins}{Number of minutes which determine distance between listenings.} 14 | } 15 | \value{ 16 | A data table containing information about length of every session. 17 | } 18 | \description{ 19 | Information about length of every session. Excludes skipped tracks. 20 | } 21 | -------------------------------------------------------------------------------- /man/make_search_queries_dt.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/make_search_queries_dt.R 3 | \name{make_search_queries_dt} 4 | \alias{make_search_queries_dt} 5 | \title{Make data table with search queries} 6 | \usage{ 7 | make_search_queries_dt(folder_path) 8 | } 9 | \arguments{ 10 | \item{folder_path}{Path to a folder which contains file with 11 | search queries in json format. File must contain 'SearchQueries" in the name.} 12 | } 13 | \value{ 14 | A data table with 3 columns(date, platform, country) 15 | containing details of Search Queries from spotify. 16 | } 17 | \description{ 18 | Make data table with search queries from Spotify data. 19 | } 20 | -------------------------------------------------------------------------------- /R/complete_streaming_history.R: -------------------------------------------------------------------------------- 1 | #' Make and prepare the streaming history data table 2 | #' 3 | #' Make data table with make_streaming_history_dt and do prepare_streaming_history with, that 4 | #' data table 5 | #' 6 | #' @param folder_path Path to a folder which contains file or files with 7 | #' Streaming History in json format. File(s) must contain "StreamingHistory" in the name. 8 | #' 9 | #' @return A data table containing streaming history from spotify suited for being used 10 | #' in rest of the functions from the package. 11 | #' 12 | #' @export 13 | 14 | 15 | complete_streaming_history <- function(folder_path) 16 | prepare_streaming_history(make_streaming_history_dt(folder_path)) 17 | 18 | 19 | -------------------------------------------------------------------------------- /man/longest_session.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/longest_session.R 3 | \name{longest_session} 4 | \alias{longest_session} 5 | \title{Longest session info} 6 | \usage{ 7 | longest_session(streaming_history, mins) 8 | } 9 | \arguments{ 10 | \item{streaming_history}{A data.table containing streaming history, after 'prepare_streaming_history' was used on it.} 11 | 12 | \item{mins}{Number of minutes which determine distance between listenings.} 13 | } 14 | \value{ 15 | A data table containing information about longest session. 16 | } 17 | \description{ 18 | Shows all information about longest session i.e. tracks, artists etc. Excludes skipped tracks. 19 | } 20 | -------------------------------------------------------------------------------- /man/sessions_intervals.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sessions_intervals.R 3 | \name{sessions_intervals} 4 | \alias{sessions_intervals} 5 | \title{Sessions length by intervals.} 6 | \usage{ 7 | sessions_intervals(streaming_history, mins) 8 | } 9 | \arguments{ 10 | \item{streaming_history}{A data.table containing streaming history, after 11 | 'prepare_streaming_history' was used on it.} 12 | 13 | \item{mins}{Number of minutes which determine distance between listenings.} 14 | } 15 | \description{ 16 | Shows table with sessions divided into listening intervals. 17 | Sessions length are divided into categories: <10min, 10-30min, 0.5-1h, 1-2h, 2-4h, 4-6h, 6h+, 18 | } 19 | -------------------------------------------------------------------------------- /tests/testthat/test-compare_history_test.R: -------------------------------------------------------------------------------- 1 | context("Compare function") 2 | 3 | testthat::test_that( 4 | "compare_history test: checks data table columns type, dimensions and compare number of rows", 5 | { 6 | a <- compare_history(spotifyviz::clean_stream_his, 7 | spotifyviz::clean_stream_his2) 8 | b <- compare_history(spotifyviz::clean_stream_his, 9 | spotifyviz::clean_stream_his2, 10 | TRUE) 11 | expect_equal(dim(a), c(28, 1)) 12 | expect_equal(dim(b), c(76, 2)) 13 | expect_true(nrow(b) >= nrow(a)) 14 | expect_is(a[[1]], "character") 15 | expect_is(b[[1]], "character") 16 | expect_is(b[[2]], "character") 17 | 18 | 19 | } 20 | ) 21 | -------------------------------------------------------------------------------- /man/sessions_visualize.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/sessions_visualize.R 3 | \name{sessions_visualize} 4 | \alias{sessions_visualize} 5 | \title{Visualize sessions length} 6 | \usage{ 7 | sessions_visualize(streaming_history, mins, as_percentage = TRUE) 8 | } 9 | \arguments{ 10 | \item{streaming_history}{A data.table containing streaming history, after 11 | 'prepare_streaming_history' was used on it.} 12 | 13 | \item{mins}{Number of minutes which determine distance between listenings.} 14 | 15 | \item{as_percentage}{A logical value. If TRUE (default) bars show percentage of of all sessions.} 16 | } 17 | \description{ 18 | Visualize sessions length divided on intervals using bar plot. 19 | } 20 | -------------------------------------------------------------------------------- /man/make_streaming_history_dt.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/make_streaming_history_dt.R 3 | \name{make_streaming_history_dt} 4 | \alias{make_streaming_history_dt} 5 | \title{Make data table with streaming history} 6 | \usage{ 7 | make_streaming_history_dt(folder_path) 8 | } 9 | \arguments{ 10 | \item{folder_path}{Path to a folder which contains file or files with 11 | Streaming History in json format. File(s) must contain "StreamingHistory" in the name.} 12 | } 13 | \value{ 14 | A data table with 4 columns(endTime, artistName, trackName, msPlayed) of characters 15 | containing details of Streaming History from spotify. 16 | } 17 | \description{ 18 | Make data table with streaming history from Spotify data. 19 | } 20 | -------------------------------------------------------------------------------- /man/plot_day_comparison.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot_day_comparison.R 3 | \name{plot_day_comparison} 4 | \alias{plot_day_comparison} 5 | \title{Compare two users day play time} 6 | \usage{ 7 | plot_day_comparison(two_users_dt, user1_name = "User 1", user2_name = "User 2") 8 | } 9 | \arguments{ 10 | \item{two_users_dt}{A data table with two users play times on a specific day, made with \code{\link{make_two_users_dt}}} 11 | 12 | \item{user1_name}{A character vector, name to display for the first user.} 13 | 14 | \item{user2_name}{A character vector, name to display for the second user.} 15 | } 16 | \description{ 17 | Compare on which times two users were listening to spotify during a specified day. 18 | } 19 | -------------------------------------------------------------------------------- /man/clean_search_queries.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/clean_search_queries.R 3 | \docType{data} 4 | \name{clean_search_queries} 5 | \alias{clean_search_queries} 6 | \title{Clean search queries} 7 | \format{ 8 | A data table with 10 rows and 3 variables: 9 | \describe{ 10 | \item{date}{Date, the day the search was performed } 11 | \item{platform}{Platform on which the search was performed} 12 | \item{country}{Country in which the search was performed} 13 | } 14 | } 15 | \usage{ 16 | clean_search_queries 17 | } 18 | \description{ 19 | A dataset with search queries,result of using make_search_queries_dt() on JSON file, 20 | selected from Wiktor's data from spotify, prepared for testing and vignettes. 21 | } 22 | \keyword{datasets} 23 | -------------------------------------------------------------------------------- /man/complete_streaming_history.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/complete_streaming_history.R 3 | \name{complete_streaming_history} 4 | \alias{complete_streaming_history} 5 | \title{Make and prepare the streaming history data table} 6 | \usage{ 7 | complete_streaming_history(folder_path) 8 | } 9 | \arguments{ 10 | \item{folder_path}{Path to a folder which contains file or files with 11 | Streaming History in json format. File(s) must contain "StreamingHistory" in the name.} 12 | } 13 | \value{ 14 | A data table containing streaming history from spotify suited for being used 15 | in rest of the functions from the package. 16 | } 17 | \description{ 18 | Make data table with make_streaming_history_dt and do prepare_streaming_history with, that 19 | data table 20 | } 21 | -------------------------------------------------------------------------------- /man/session_for_view.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/session_for_view.R 3 | \name{session_for_view} 4 | \alias{session_for_view} 5 | \title{Change session data table for viewing} 6 | \usage{ 7 | session_for_view(session_dt) 8 | } 9 | \arguments{ 10 | \item{session_dt}{A data table containing information about listening session. 11 | Must have columns "artist_name", "track_name", "s_played", "start_time".} 12 | } 13 | \description{ 14 | Changes the data table containing informations about a session (for example form 15 | \code{\link{longest_session}}) to be in a format 16 | good for reading - spaces instead of "_" in column names, time of playing in 17 | "Xh Ymin Zs" format, and have only columns with artist name, track name, track start and time played. 18 | } 19 | -------------------------------------------------------------------------------- /man/str_his_with_playlists_long.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/str_hist_with_playlists_long.R 3 | \name{str_his_with_playlists_long} 4 | \alias{str_his_with_playlists_long} 5 | \title{Prepares the streaming history with playlists data table} 6 | \usage{ 7 | str_his_with_playlists_long(playlists_dt, streaming_history) 8 | } 9 | \arguments{ 10 | \item{playlists_dt}{A data table containing the names of playlists and songs on them from spotify.} 11 | 12 | \item{streaming_history}{A data table containing streaming history from spotify.} 13 | } 14 | \value{ 15 | A data table containing streaming history from spotify with information on which 16 | playlists song is. Data table is in long format. 17 | } 18 | \description{ 19 | Prepares the streaming history with playlists data table 20 | } 21 | -------------------------------------------------------------------------------- /man/str_his_with_playlists_wide.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/str_hist_with_playlists_wide.R 3 | \name{str_his_with_playlists_wide} 4 | \alias{str_his_with_playlists_wide} 5 | \title{Prepares the streaming history with playlists data table} 6 | \usage{ 7 | str_his_with_playlists_wide(playlists_dt, streaming_history) 8 | } 9 | \arguments{ 10 | \item{playlists_dt}{A data table containing the names of playlists and songs on them from spotify.} 11 | 12 | \item{streaming_history}{A data table containing streaming history from spotify.} 13 | } 14 | \value{ 15 | A data table containing streaming history from spotify with information on which 16 | playlists song is. Data table is in wide format. 17 | } 18 | \description{ 19 | Prepares the streaming history with playlists data table 20 | } 21 | -------------------------------------------------------------------------------- /man/compare_history.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/compare_history.R 3 | \name{compare_history} 4 | \alias{compare_history} 5 | \title{Common artists / artists and tracks for two users.} 6 | \usage{ 7 | compare_history(streaming_history_1, streaming_history_2, by_track = F) 8 | } 9 | \arguments{ 10 | \item{streaming_history_1}{A data.table containing streaming history, 11 | after 'prepare_streaming_history' was used on it from user 1.} 12 | 13 | \item{streaming_history_2}{A data.table containing streaming history, 14 | after 'prepare_streaming_history' was used on it from user 2.} 15 | 16 | \item{by_track}{Logical value, whether comparison should include tracks. Default is FALSE.} 17 | } 18 | \description{ 19 | Shows the same artists/ artists and tracks from history of two different users. 20 | } 21 | -------------------------------------------------------------------------------- /R/make_streaming_history_dt.R: -------------------------------------------------------------------------------- 1 | #' Make data table with streaming history 2 | #' 3 | #' Make data table with streaming history from Spotify data. 4 | #' 5 | #' @param folder_path Path to a folder which contains file or files with 6 | #' Streaming History in json format. File(s) must contain "StreamingHistory" in the name. 7 | #' 8 | #' @return A data table with 4 columns(endTime, artistName, trackName, msPlayed) of characters 9 | #' containing details of Streaming History from spotify. 10 | #' @export 11 | #' 12 | #' @import data.table 13 | #' @import jsonlite 14 | 15 | 16 | make_streaming_history_dt <- function(folder_path) { 17 | files_path <- list.files(folder_path, "StreamingHistory.*\\.json$") 18 | if (folder_path != ".") 19 | files_path <- paste(folder_path, files_path, sep = "/") 20 | unique(rbindlist(lapply(files_path, jsonlite::fromJSON))) 21 | } 22 | -------------------------------------------------------------------------------- /R/sessions_length.R: -------------------------------------------------------------------------------- 1 | #' Sessions length 2 | #' 3 | #' Information about length of every session. Excludes skipped tracks. 4 | #' 5 | #' 6 | #' @param streaming_history A data.table containing streaming history, after 7 | #' 'prepare_streaming_history' was used on it. 8 | #' @param mins Number of minutes which determine distance between listenings. 9 | #' @return A data table containing information about length of every session. 10 | #' 11 | #' @export 12 | #' 13 | #' @import data.table 14 | 15 | 16 | 17 | 18 | 19 | sessions_length = function(streaming_history, mins) { 20 | 21 | skipped <- . <- s_played <- listening_number <- NULL 22 | 23 | con_list_dt <- continuous_listening(streaming_history[skipped == FALSE, ], mins) 24 | 25 | sessions <- con_list_dt[, .(session_time = sum(s_played)), by = listening_number] 26 | 27 | sessions 28 | 29 | } -------------------------------------------------------------------------------- /R/make_search_queries_dt.R: -------------------------------------------------------------------------------- 1 | #' Make data table with search queries 2 | #' 3 | #' Make data table with search queries from Spotify data. 4 | #' 5 | #' @param folder_path Path to a folder which contains file with 6 | #' search queries in json format. File must contain 'SearchQueries" in the name. 7 | #' 8 | #' @return A data table with 3 columns(date, platform, country) 9 | #' containing details of Search Queries from spotify. 10 | #' @export 11 | #' 12 | #' @import data.table 13 | #' @import jsonlite 14 | #' @importFrom lubridate ymd 15 | 16 | make_search_queries_dt <- function(folder_path) { 17 | platform <- country <- . <- NULL 18 | 19 | file_path <- list.files(folder_path, "SearchQueries.*\\.json$") 20 | if (folder_path != ".") { 21 | file_path <- paste(folder_path, file_path, sep = "/") 22 | } 23 | read_file <- data.table(jsonlite::fromJSON(file_path)) 24 | read_file[, .(date = ymd(date), platform, country)] 25 | } 26 | -------------------------------------------------------------------------------- /man/make_two_users_dt.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/make_two_users_dt.R 3 | \name{make_two_users_dt} 4 | \alias{make_two_users_dt} 5 | \title{Make data table to use in \code{plot_day_comparison}} 6 | \usage{ 7 | make_two_users_dt(user1_data, user2_data, day) 8 | } 9 | \arguments{ 10 | \item{user1_data}{A data.table containing streaming history from first user, 11 | made with \code{\link{complete_streaming_history}}.} 12 | 13 | \item{user2_data}{A data.table containing streaming history from second user, 14 | made with \code{\link{complete_streaming_history}}.} 15 | 16 | \item{day}{end_date A POSIXt, Date or string that can be coerced into 17 | Date by \code{\link{as_date}} indicating the day for witch to create the data table.} 18 | } 19 | \description{ 20 | Make data table with data about play time of two users on given day, 21 | for further use in \code{\link{plot_day_comparison}} 22 | } 23 | -------------------------------------------------------------------------------- /man/plot_track_count.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot_track_count.R 3 | \name{plot_track_count} 4 | \alias{plot_track_count} 5 | \title{visualize number of tracks that were played or skipped} 6 | \usage{ 7 | plot_track_count(streaming_history, only_skipped = FALSE, by = "year") 8 | } 9 | \arguments{ 10 | \item{streaming_history}{A data.table containing streaming history, 11 | after 'prepare_streaming_history' was used on it.} 12 | 13 | \item{only_skipped}{A logical scalar indicating whether to show 14 | all played tracks or only those that were skipped.} 15 | 16 | \item{by}{A character vector indicating for which periods 17 | to split the time for counting tracks played/skipped. 18 | Permitted values are: "year", "month", "week", "day".} 19 | } 20 | \description{ 21 | visualizes, using a bar chart, how many tracks were played or how many were skipped, each period (day, week, month, year). 22 | } 23 | -------------------------------------------------------------------------------- /man/continuous_listening.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/continuous_listening.R 3 | \name{continuous_listening} 4 | \alias{continuous_listening} 5 | \title{Divides streaming history into a groups of song which where listened one after another.} 6 | \usage{ 7 | continuous_listening(streaming_history, mins) 8 | } 9 | \arguments{ 10 | \item{streaming_history}{A data table containing streaming history from spotify.} 11 | 12 | \item{mins}{Number of minutes which determine distance between listening sessions.} 13 | } 14 | \value{ 15 | A data table containing streaming history with additional column about listening number. 16 | } 17 | \description{ 18 | The listening number indicates the session number. 19 | If distance between beginning of a song and end of a previous one is less than 20 | \code{mins} then listening number stays. 21 | Otherwise increases. Counting begins from the oldest song in streaming history. 22 | } 23 | -------------------------------------------------------------------------------- /R/filter_search_queries.R: -------------------------------------------------------------------------------- 1 | #' Filter spotify search queries 2 | #' 3 | #' Filter spotify search queries data table to contain only entries from specified time period 4 | #' 5 | #' @param search_queries A data table with search queries, 6 | #' made with \code{\link{make_search_queries_dt}} 7 | #' @param start_date A POSIXt,Date or string that can be coerced 8 | #' into Date by \code{\link{as_date}} indicating start of the period of time. 9 | #' @param end_date A POSIXt, Date or string that can be coerced 10 | #' into Date by \code{\link{as_date}} indicating end of the period of time. 11 | #' 12 | #' @return A search queries data table containing only entries between \code{start_date} and 13 | #' \code{end_date} 14 | #' 15 | #' @export 16 | #' 17 | #' @seealso \code{\link{filter_streaming_history}} 18 | 19 | 20 | filter_search_queries <- function(search_queries, start_date, end_date) { 21 | search_queries[date <= as_date(end_date) & date >= as_date(start_date), ] 22 | } 23 | -------------------------------------------------------------------------------- /man/most_played.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/most_played.R 3 | \name{most_played} 4 | \alias{most_played} 5 | \title{Most played tracks/artist} 6 | \usage{ 7 | most_played( 8 | streaming_history, 9 | track_or_artist, 10 | how_many = 10, 11 | show_skipped = TRUE 12 | ) 13 | } 14 | \arguments{ 15 | \item{streaming_history}{A data.table containing streaming history, after 'prepare_streaming_history' was used on it.} 16 | 17 | \item{track_or_artist}{A character string that specifies whether to show most frequently played artists or tracks. 18 | Must be either "track" or "artist"} 19 | 20 | \item{how_many}{A positive integer indicating how many most frequent tracks/artist to show, defaults to 10.} 21 | 22 | \item{show_skipped}{A logic scalar indicating whether to include tracks that were skipped in counting.} 23 | } 24 | \description{ 25 | Shows most frequently played artist or track and counts how many times were they played. 26 | } 27 | -------------------------------------------------------------------------------- /R/theme_spotifyviz.R: -------------------------------------------------------------------------------- 1 | #' Spotifyviz theme for ggplot2 2 | #' 3 | #' Theme used in plots. 4 | #' 5 | #' @param base_size Base font size, given in pts. 6 | #' @param base_family Base font family. 7 | #' @param base_line_size Base size for line elements 8 | #' @param base_rect_size Base size for rect elements 9 | #' 10 | #' @export 11 | #' 12 | theme_spotifyviz <- function(base_size = 16, base_family = "", 13 | base_line_size = base_size / 22, 14 | base_rect_size = base_size / 22) { 15 | # Starts with theme_bw and then modify some parts 16 | theme_bw( 17 | base_size = base_size, 18 | base_family = base_family, 19 | base_line_size = base_line_size, 20 | base_rect_size = base_rect_size 21 | ) %+replace% 22 | theme(strip.background = element_rect(colour = "black", fill = "white"), 23 | plot.title = element_text(hjust = 0.5), 24 | plot.subtitle = element_text(hjust = 0.5), 25 | complete = TRUE 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: spotifyviz 2 | Title: Visualize Spotify Data 3 | Version: 0.0.0.9000 4 | Authors@R: 5 | c(person(given = "Magdalena", 6 | family = "Buszka", 7 | role = c("aut", "cre"), 8 | email = "Magdalena.Buszka@math.uni.wroc.pl"), 9 | person(given = "Artur", 10 | family = "Simon", 11 | role = "aut", 12 | email = "artur.simon72@gmail.com"), 13 | person(given = "Wiktor", 14 | family = "Jacaszek", 15 | role = "aut", 16 | email = "Wiktor.Jacaszek@math.uni.wroc.pl") 17 | ) 18 | Description: Visualize and summarize data from Spotify. 19 | License: GPL-3 20 | Encoding: UTF-8 21 | LazyData: true 22 | RoxygenNote: 7.1.0 23 | Imports: 24 | data.table, 25 | lubridate, 26 | ggplot2, 27 | jsonlite, 28 | viridisLite 29 | Suggests: 30 | spelling, 31 | knitr, 32 | rmarkdown, 33 | testthat 34 | Language: en-US 35 | VignetteBuilder: knitr 36 | Depends: 37 | R (>= 2.10) 38 | -------------------------------------------------------------------------------- /man/filter_search_queries.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filter_search_queries.R 3 | \name{filter_search_queries} 4 | \alias{filter_search_queries} 5 | \title{Filter spotify search queries} 6 | \usage{ 7 | filter_search_queries(search_queries, start_date, end_date) 8 | } 9 | \arguments{ 10 | \item{search_queries}{A data table with search queries, 11 | made with \code{\link{make_search_queries_dt}}} 12 | 13 | \item{start_date}{A POSIXt,Date or string that can be coerced 14 | into Date by \code{\link{as_date}} indicating start of the period of time.} 15 | 16 | \item{end_date}{A POSIXt, Date or string that can be coerced 17 | into Date by \code{\link{as_date}} indicating end of the period of time.} 18 | } 19 | \value{ 20 | A search queries data table containing only entries between \code{start_date} and 21 | \code{end_date} 22 | } 23 | \description{ 24 | Filter spotify search queries data table to contain only entries from specified time period 25 | } 26 | \seealso{ 27 | \code{\link{filter_streaming_history}} 28 | } 29 | -------------------------------------------------------------------------------- /man/plot_playlist_popularity.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot_playlist_popularity.R 3 | \name{plot_playlist_popularity} 4 | \alias{plot_playlist_popularity} 5 | \title{Visualize popularity of different playlist} 6 | \usage{ 7 | plot_playlist_popularity( 8 | str_his_with_playlists_long, 9 | time_or_count = "time", 10 | time_unit = "hours" 11 | ) 12 | } 13 | \arguments{ 14 | \item{str_his_with_playlists_long}{A data table containing streaming history with information about playlists, 15 | in long form, made with \code{\link{str_his_with_playlists_long}}.} 16 | 17 | \item{time_or_count}{A character vector indicating whether to count number of tracks played 18 | or the time they were played.} 19 | 20 | \item{time_unit}{A character vector indicating the time unit to use if showing an absolute value of time played. 21 | Permitted vales are "seconds", "minutes", "hours".} 22 | } 23 | \description{ 24 | Visualize, using a bar chart, how popular were different playlists. Either by number of tracks played or play time. 25 | } 26 | -------------------------------------------------------------------------------- /R/session_for_view.R: -------------------------------------------------------------------------------- 1 | #'Change session data table for viewing 2 | #' 3 | #'Changes the data table containing informations about a session (for example form 4 | #'\code{\link{longest_session}}) to be in a format 5 | #' good for reading - spaces instead of "_" in column names, time of playing in 6 | #' "Xh Ymin Zs" format, and have only columns with artist name, track name, track start and time played. 7 | #' 8 | #' @param session_dt A data table containing information about listening session. 9 | #' Must have columns "artist_name", "track_name", "s_played", "start_time". 10 | #' 11 | #' @export 12 | #' 13 | #' 14 | #' 15 | #' 16 | session_for_view <- function(session_dt) { 17 | . = NULL 18 | 19 | track_name <- artist_name <- s_played <- start_time <- NULL 20 | 21 | session_dt <- 22 | session_dt[, .( 23 | `Start` = lubridate::floor_date(start_time, "minutes"), 24 | `Artist name` = artist_name, 25 | `Track name` = track_name, 26 | `Time played` = from_sec_to_hms(as.integer(s_played)) 27 | )] 28 | session_dt 29 | } -------------------------------------------------------------------------------- /R/filter_streaming_history.R: -------------------------------------------------------------------------------- 1 | #' Filter streaming history 2 | #' 3 | #' Filer streaming history data table to only contain information about a given period of time. 4 | #' 5 | #' @param streaming_history A data.table containing streaming history, 6 | #' made with \code{\link{complete_streaming_history}}. 7 | #' @param start_date start_date A POSIXt,Date or string that can be coerced 8 | #' into Date by \code{\link{as_date}} indicating start of the period of time. 9 | #' @param end_date end_date A POSIXt, Date or string that can be coerced 10 | #' into Date by \code{\link{as_date}} indicating end of the period of time. 11 | #' 12 | #' @return A streaming history data table containing only entries between 13 | #' \code{start_date} and \code{end_date} 14 | #' @export 15 | #' 16 | #' @seealso \code{\link{filter_search_queries}} 17 | 18 | 19 | filter_streaming_history <- function(streaming_history, start_date, end_date) { 20 | start_time <- end_time <- NULL 21 | streaming_history[as_date(start_date) <= start_time & 22 | as_date(end_date) + duration(86399, "second") >= end_time, ] 23 | } 24 | -------------------------------------------------------------------------------- /R/longest_session.R: -------------------------------------------------------------------------------- 1 | #' Longest session info 2 | #' 3 | #' Shows all information about longest session i.e. tracks, artists etc. Excludes skipped tracks. 4 | #' 5 | #' 6 | #' @param streaming_history A data.table containing streaming history, after 'prepare_streaming_history' was used on it. 7 | #' @param mins Number of minutes which determine distance between listenings. 8 | 9 | #' 10 | #' @return A data table containing information about longest session. 11 | #' 12 | #' @export 13 | #' 14 | #' @import data.table 15 | 16 | 17 | 18 | 19 | 20 | longest_session <- function(streaming_history, mins) { 21 | . <- listening_number <- s_played <- session_time <- skipped <- NULL 22 | str_his <- copy(streaming_history) 23 | con_list_dt <- continuous_listening(str_his[skipped == FALSE, ], mins) 24 | sessions <- con_list_dt[, .(session_time = sum(s_played)), by = listening_number] 25 | longest_session_number <- sessions[session_time == max(session_time), ][[1, 1]] 26 | longest_session_dt <- con_list_dt[listening_number == longest_session_number, ] 27 | longest_session_dt 28 | } 29 | -------------------------------------------------------------------------------- /R/make_session_stats.R: -------------------------------------------------------------------------------- 1 | #' Make session stats table 2 | #' 3 | #' Make a table containing stats about session - play time , number of tracks played, start and end. 4 | #' 5 | #' @param session_dt A data table containing information about listening session. 6 | #' Must have columns "artist_name", "track_name", "s_played", "start_time". 7 | #' 8 | #' @export 9 | #' @importFrom data.table data.table 10 | #' 11 | #' 12 | 13 | make_session_stats <- function(session_dt) { 14 | start_time <- end_time <- . <- s_played <- NULL 15 | table <- data.table::data.table( 16 | `Session start` = substr( 17 | as.character(session_dt[, lubridate::floor_date(min(start_time), "minutes")]), 18 | start = 1, 19 | stop = 16 20 | ), 21 | `Session end` = substr( 22 | as.character(session_dt[, lubridate::floor_date(max(end_time), "minutes")]), 23 | start = 1, 24 | stop = 16 25 | ), 26 | `Tracks played` = c(session_dt[, .N]), 27 | `Play time` = c(session_dt[, .(from_sec_to_hms(as.integer(sum(s_played))))]) 28 | ) 29 | table 30 | } 31 | 32 | 33 | -------------------------------------------------------------------------------- /man/make_summary_dt.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/make_summary_dt.R 3 | \name{make_summary_dt} 4 | \alias{make_summary_dt} 5 | \title{Summarize streaming history} 6 | \usage{ 7 | make_summary_dt(streaming_history, start_date, end_date, as_percentage = TRUE) 8 | } 9 | \arguments{ 10 | \item{streaming_history}{A data.table containing streaming history, after 11 | 'prepare_streaming_history' was used on it.} 12 | 13 | \item{start_date}{A POSIXt,Date or string that can be coerced into 14 | Date by \code{\link{as_date}} indicating start of the period of time.} 15 | 16 | \item{end_date}{A POSIXt,Date or string that can be coerced into 17 | Date by \code{\link{as_date}} indicating end of the period of time.} 18 | 19 | \item{as_percentage}{A logical scalar. If \code{TRUE} (default) 20 | tracks skipped and time listened to will be a percentage of all tracks and whole time period.} 21 | } 22 | \value{ 23 | A data table with summarized streaming history 24 | } 25 | \description{ 26 | Make table with summary stats about streaming history in given data period. 27 | } 28 | -------------------------------------------------------------------------------- /man/most_skipped.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/most_skipped.R 3 | \name{most_skipped} 4 | \alias{most_skipped} 5 | \title{Most skipped tracks/artist} 6 | \usage{ 7 | most_skipped( 8 | streaming_history, 9 | track_or_artist, 10 | how_many = 10, 11 | for_view = TRUE 12 | ) 13 | } 14 | \arguments{ 15 | \item{streaming_history}{A data.table containing streaming history, 16 | 17 | after 'prepare_streaming_history' was used on it.} 18 | 19 | \item{track_or_artist}{A character string that specifies whether 20 | to show most frequently skipped artists or tracks. 21 | Must be either "track" or "artist"} 22 | 23 | \item{how_many}{A positive integer indicating how many most skipped 24 | tracks/artist to show, defaults to 10.} 25 | 26 | \item{for_view}{A logical scalar. If \code{TRUE} (default) returns a 27 | data.table that is easy to read for humans,with spaces in column names and capitalized first letters} 28 | } 29 | \description{ 30 | Shows most frequently skipped artist or track in given time period and counts how many times were they skipped 31 | } 32 | -------------------------------------------------------------------------------- /man/filter_streaming_history.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/filter_streaming_history.R 3 | \name{filter_streaming_history} 4 | \alias{filter_streaming_history} 5 | \title{Filter streaming history} 6 | \usage{ 7 | filter_streaming_history(streaming_history, start_date, end_date) 8 | } 9 | \arguments{ 10 | \item{streaming_history}{A data.table containing streaming history, 11 | made with \code{\link{complete_streaming_history}}.} 12 | 13 | \item{start_date}{start_date A POSIXt,Date or string that can be coerced 14 | into Date by \code{\link{as_date}} indicating start of the period of time.} 15 | 16 | \item{end_date}{end_date A POSIXt, Date or string that can be coerced 17 | into Date by \code{\link{as_date}} indicating end of the period of time.} 18 | } 19 | \value{ 20 | A streaming history data table containing only entries between 21 | \code{start_date} and \code{end_date} 22 | } 23 | \description{ 24 | Filer streaming history data table to only contain information about a given period of time. 25 | } 26 | \seealso{ 27 | \code{\link{filter_search_queries}} 28 | } 29 | -------------------------------------------------------------------------------- /man/prepare_streaming_history.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/prepare_streaming_history.R 3 | \name{prepare_streaming_history} 4 | \alias{prepare_streaming_history} 5 | \title{Prepares the streaming history data table} 6 | \usage{ 7 | prepare_streaming_history(streaming_history) 8 | } 9 | \arguments{ 10 | \item{streaming_history}{A raw data table containing streaming history from spotify.} 11 | } 12 | \value{ 13 | A data table containing streaming history from spotify suited for being used 14 | in rest of the functions from the package. 15 | } 16 | \description{ 17 | Prepares the data table containing streaming history for further operations. 18 | Changes the names of the base columns to: end_time, artist_name, track_name, s_played. 19 | Changes the end_time 20 | column from character to POSIXt date, s_played to a duration in seconds. Adds new columns: 21 | start_time - estimated start time calculated from end_time and s_played, skipped - 22 | TRUE if the track was 23 | played for less than 10s, weekday - weekdays derived from start_time date. \cr 24 | } 25 | -------------------------------------------------------------------------------- /tests/testthat/test-reading_functions.R: -------------------------------------------------------------------------------- 1 | context("Reading JSON Files") 2 | 3 | testthat::test_that("StreamingHistory reading test: dimensions, column types and type of object", { 4 | p <- make_streaming_history_dt("DataFromSpotify") 5 | expect_equal(dim(p), c(100, 4)) 6 | expect_is(p[[1]], "character") 7 | expect_is(p[[2]], "character") 8 | expect_is(p[[3]], "character") 9 | expect_is(p[[4]], "integer") 10 | expect_is(p, "data.table") 11 | 12 | }) 13 | 14 | testthat::test_that("SearchQueries reading test: dimensions, column types and type of object", { 15 | p <- make_search_queries_dt("DataFromSpotify") 16 | expect_equal(dim(p), c(10, 3)) 17 | expect_is(p[[1]], "Date") 18 | expect_is(p[[2]], "character") 19 | expect_is(p[[3]], "character") 20 | expect_is(p, "data.table") 21 | }) 22 | 23 | testthat::test_that("prepare_playlist reading test: dimensions, column types and type of object", { 24 | p <- prepare_playlists("DataFromSpotify") 25 | expect_equal(dim(p), c(199, 3)) 26 | expect_is(p[[1]], "character") 27 | expect_is(p[[2]], "character") 28 | expect_is(p[[3]], "character") 29 | expect_is(p, "data.table") 30 | }) 31 | -------------------------------------------------------------------------------- /man/count_skipped.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/count_skipped.R 3 | \name{count_skipped} 4 | \alias{count_skipped} 5 | \title{Count skipped songs in given time period} 6 | \usage{ 7 | count_skipped(streaming_history, start_date, end_date, as_percentage = FALSE) 8 | } 9 | \arguments{ 10 | \item{streaming_history}{data.table containing streaming history, 11 | after 'prepare_streaming_history' was used on it} 12 | 13 | \item{start_date}{A POSIXt,Date or string that can be coerced into Date 14 | by \code{\link{as_date}} indicating start of the period of time to count skipped songs} 15 | 16 | \item{end_date}{A POSIXt,Date or string that can be coerced into Date 17 | by \code{\link{as_date}} indicating end of the period of time to count skipped songs} 18 | 19 | \item{as_percentage}{logical. if FALSE (default) a number of skipped songs is returned, 20 | otherwise a character vector indicating percentage of skipped songs in given period} 21 | } 22 | \description{ 23 | Counts how many songs were skipped in given time period. Returns either a number or a percentage of all songs 24 | in given time period depending on user 25 | } 26 | -------------------------------------------------------------------------------- /R/plot_searches.R: -------------------------------------------------------------------------------- 1 | #' Visualize search queries 2 | #' 3 | #' @param search_queries A data table containing spotify search queries, made with \code{\link{make_search_queries_dt}} 4 | #' @param additional A character vector, additional information to be visualized, can be either "country" or "platform". 5 | #' 6 | #' @export 7 | plot_searches <- function(search_queries, additional = "country") { 8 | 9 | . <- N <- country <- platform <- NULL 10 | 11 | count <- search_queries[, .N, by = .(date, country, platform)] 12 | max <- count[, max(N)] 13 | 14 | viz <- ggplot(count) + 15 | geom_point(aes( 16 | y = N, 17 | x = date, 18 | colour = eval(as.name(additional)) 19 | ), 20 | size = 3, 21 | shape = "circle") + 22 | theme_spotifyviz() + 23 | scale_x_date(date_minor_breaks = "1 day") + 24 | scale_y_continuous(breaks = seq(0, max + 1 , by = 2), 25 | minor_breaks = 0:max) + 26 | theme(legend.position = "bottom") + 27 | labs(x = "Date", y = "Searches") + 28 | scale_color_manual(name = paste(capitalize(additional), ":"), 29 | values = c("#440154FF", "#7AD151FF")) 30 | 31 | viz 32 | 33 | } 34 | -------------------------------------------------------------------------------- /R/sessions_intervals.R: -------------------------------------------------------------------------------- 1 | #' Sessions length by intervals. 2 | #' 3 | #' 4 | #' Shows table with sessions divided into listening intervals. 5 | #' Sessions length are divided into categories: <10min, 10-30min, 0.5-1h, 1-2h, 2-4h, 4-6h, 6h+, 6 | #' 7 | #' 8 | #' @param streaming_history A data.table containing streaming history, after 9 | #' 'prepare_streaming_history' was used on it. 10 | #' @param mins Number of minutes which determine distance between listenings. 11 | #' @export 12 | #' 13 | #' @import data.table 14 | 15 | 16 | 17 | sessions_intervals <- function(streaming_history, mins) { 18 | sum <- frac <- . <- NULL 19 | 20 | sessions <- sessions_length(streaming_history, mins) 21 | 22 | sessions$interval <- cut( 23 | sessions$session_time, 24 | breaks = c(1, 10 * 60, 30 * 60, 60 * 60, 2 * 60 * 60, 4 * 60 * 60, 6 * 60 * 60, 25 | (max( 26 | sessions$session_time 27 | ) + 1)), 28 | labels = c("<10min", "10-30min", "0.5-1h", "1-2h", "2-4h", "4-6h", "6h+") 29 | ) 30 | 31 | sessions_groupped <- sessions[, .(sum = .N), by = interval][, frac := 100 * round(sum / sum(sum), 3)] 32 | 33 | sessions_groupped 34 | } -------------------------------------------------------------------------------- /R/prepare_playlists.R: -------------------------------------------------------------------------------- 1 | #' Prepare playlists data table 2 | #' 3 | #' @param folder_path Path to a folder which contains file with Playlists in json format. 4 | #' File must contain "Playlist" in the name. 5 | #' 6 | #' @return A data table containing the names of playlists and songs on them from spotify. 7 | #' @export 8 | #' 9 | #' @import data.table 10 | #' @import jsonlite 11 | 12 | prepare_playlists <- function(folder_path) { 13 | song_names_function <- function(x) { 14 | return(df[[3]][[x]][[1]][[1]]) 15 | } 16 | artist_names_function <- function(x) { 17 | return(df[[3]][[x]][[1]][[2]]) 18 | } 19 | 20 | files_path <- list.files(folder_path, "Playlist.*\\.json$") 21 | if (folder_path != ".") 22 | files_path <- paste(folder_path, files_path, sep = "/") 23 | df <- jsonlite::fromJSON(files_path) 24 | df <- as.data.table(df) 25 | 26 | 27 | playlists<- lapply(seq_len(nrow(df)), function(x) { 28 | data.table( 29 | playlist_name = df[x, 1], 30 | track_name = song_names_function(x), 31 | artist_name = artist_names_function(x) 32 | ) 33 | }) 34 | 35 | playlists_dt = rbindlist(playlists) 36 | 37 | playlists_dt 38 | } 39 | -------------------------------------------------------------------------------- /R/str_hist_with_playlists_long.R: -------------------------------------------------------------------------------- 1 | #' Prepares the streaming history with playlists data table 2 | #' 3 | #' 4 | #' 5 | #' @param playlists_dt A data table containing the names of playlists and songs on them from spotify. 6 | #' @param streaming_history A data table containing streaming history from spotify. 7 | #' @return A data table containing streaming history from spotify with information on which 8 | #' playlists song is. Data table is in long format. 9 | #' @export 10 | #' 11 | #' @import data.table 12 | 13 | 14 | 15 | str_his_with_playlists_long <- function(playlists_dt, streaming_history) { 16 | 17 | str_his_comp <- copy(streaming_history) 18 | 19 | track_name <- artist_name <- NULL 20 | setkey(str_his_comp, track_name, artist_name) 21 | setkey(playlists_dt, track_name, artist_name) 22 | 23 | 24 | str_hist <- playlists_dt[str_his_comp] 25 | full <- unique(str_hist) 26 | 27 | setnames( 28 | full, 29 | c( 30 | "playlist_name", 31 | "track_name", 32 | "artist_name", 33 | "end_time", 34 | "s_played", 35 | "start_time", 36 | "skipped", 37 | "weekday" 38 | ) 39 | ) 40 | 41 | full 42 | 43 | } 44 | 45 | -------------------------------------------------------------------------------- /tests/testthat/test-cleaning_functions.R: -------------------------------------------------------------------------------- 1 | context("Cleaning functions") 2 | testthat::test_that("continuous_listening test: checks numbers of session", { 3 | expect_equal(continuous_listening(spotifyviz::clean_stream_his, 10)[[8]][1:30], 4 | c(rep.int(1, 29), 2)) 5 | expect_equal(continuous_listening(spotifyviz::clean_stream_his[30], 10)[[8]], 1) 6 | }) 7 | 8 | testthat::test_that("filter_search_queries function test: checks filtering by date", { 9 | expect_true(all.equal( 10 | spotifyviz::clean_search_queries, 11 | filter_search_queries(spotifyviz::clean_search_queries, "19.09.10", "19.10.10") 12 | )) 13 | expect_true(all.equal( 14 | spotifyviz::clean_search_queries[1:3], 15 | filter_search_queries(clean_search_queries, "19.09.30", "19.09.30") 16 | )) 17 | }) 18 | 19 | testthat::test_that("filter_streaming_history function test: checks filtering by date", { 20 | expect_true(all.equal( 21 | spotifyviz::clean_stream_his, 22 | filter_streaming_history(spotifyviz::clean_stream_his, "18.11.04", "18.11.05") 23 | )) 24 | expect_true(all.equal( 25 | spotifyviz::clean_stream_his[56:100], 26 | filter_streaming_history(spotifyviz::clean_stream_his, "18.11.05", "18.11.10") 27 | )) 28 | }) 29 | -------------------------------------------------------------------------------- /man/plot_in_playlists_count.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot_in_playlists_count.R 3 | \name{plot_in_playlists_count} 4 | \alias{plot_in_playlists_count} 5 | \title{Visualize how much of music listened to was from playlists} 6 | \usage{ 7 | plot_in_playlists_count( 8 | str_his_with_playlist_wide, 9 | as_percentage = FALSE, 10 | time_or_count = "time", 11 | time_unit = "minutes" 12 | ) 13 | } 14 | \arguments{ 15 | \item{str_his_with_playlist_wide}{A data table containing streaming history 16 | with information about playlists, in wide form, made with \code{\link{str_his_with_playlists_wide}}.} 17 | 18 | \item{as_percentage}{A logical scalar indicating whether to show a percentage or an absolute value.} 19 | 20 | \item{time_or_count}{A character vector indicating whether to count 21 | number of tracks played or the time they were played.} 22 | 23 | \item{time_unit}{A character vector indicating the time unit to use 24 | if showing an absolute value of time played. Permitted vales are "seconds", "minutes", "hours".} 25 | } 26 | \description{ 27 | Visualize, using a bar chart, how much music listened to was from playlists. 28 | Either counting the number of tracks played or play time. 29 | } 30 | -------------------------------------------------------------------------------- /tests/testthat/test-formatting_functions.R: -------------------------------------------------------------------------------- 1 | context("Formatting functions") 2 | 3 | 4 | testthat::test_that("capitalize function test: check function effect for three different examples", 5 | { 6 | expect_equal(capitalize("abc"), "Abc") 7 | expect_equal(capitalize("Abc"), "Abc") 8 | expect_equal(capitalize(" abc"), " abc") 9 | }) 10 | 11 | 12 | testthat::test_that("from_sec_to_hms function test:check function effect for four different examples", 13 | { 14 | expect_true(from_sec_to_hms(50) == " 50 s") 15 | expect_true(from_sec_to_hms(1220) == " 20 min 20 s") 16 | expect_true(from_sec_to_hms(1234568) == " 342 h 56 min 8 s") 17 | expect_true(from_sec_to_hms(0) == "0s") 18 | }) 19 | 20 | 21 | testthat::test_that("session_for_view function test: check column names and dimensions ", { 22 | a <- session_for_view(spotifyviz::clean_stream_his) 23 | expect_equal(colnames(a), 24 | c("Start", "Artist name", "Track name", "Time played")) 25 | expect_equal(nrow(a), nrow(spotifyviz::clean_stream_his)) 26 | expect_equal(ncol(a), 4) 27 | }) 28 | -------------------------------------------------------------------------------- /man/plot_track_count_by_period.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot_track_count_by_period.R 3 | \name{plot_track_count_by_period} 4 | \alias{plot_track_count_by_period} 5 | \title{Visualize number of tracks played by period} 6 | \usage{ 7 | plot_track_count_by_period( 8 | filtered, 9 | period = "weekday", 10 | as_percentage = TRUE, 11 | include_skipped = FALSE, 12 | by_weekday = FALSE 13 | ) 14 | } 15 | \arguments{ 16 | \item{filtered}{A data.table containing streaming history, after 'prepare_streaming_history' 17 | was used on it.} 18 | 19 | \item{period}{A character vector indicating for which periods to split the time for 20 | counting tracks played. 21 | Permitted values are: "weekday", "hour"} 22 | 23 | \item{as_percentage}{A logical scalar. If TRUE (default) bars show percentage 24 | of all tracks played for each weekday.} 25 | 26 | \item{include_skipped}{A logical scalar indicating whether to include tracks that were skipped} 27 | 28 | \item{by_weekday}{A logical scalar indicating whether to split the graph by weekdays 29 | if period is "hour"} 30 | } 31 | \description{ 32 | Visualizes, using a bar chart, the number or percentage of songs that were played on different hours/weekdays. 33 | If split by hours, can be also faceted by weekday. 34 | } 35 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(compare_history) 4 | export(complete_streaming_history) 5 | export(continuous_listening) 6 | export(count_skipped) 7 | export(filter_search_queries) 8 | export(filter_streaming_history) 9 | export(from_sec_to_hms) 10 | export(longest_session) 11 | export(make_search_queries_dt) 12 | export(make_session_stats) 13 | export(make_streaming_history_dt) 14 | export(make_summary_dt) 15 | export(make_two_users_dt) 16 | export(measure_how_long_listened) 17 | export(most_played) 18 | export(most_skipped) 19 | export(plot_day_comparison) 20 | export(plot_in_playlists_count) 21 | export(plot_playlist_popularity) 22 | export(plot_searches) 23 | export(plot_track_count) 24 | export(plot_track_count_by_period) 25 | export(prepare_playlists) 26 | export(prepare_streaming_history) 27 | export(session_for_view) 28 | export(sessions_intervals) 29 | export(sessions_length) 30 | export(sessions_visualize) 31 | export(str_his_with_playlists_long) 32 | export(str_his_with_playlists_wide) 33 | export(theme_spotifyviz) 34 | import(data.table) 35 | import(ggplot2) 36 | import(jsonlite) 37 | import(lubridate) 38 | importFrom(data.table,data.table) 39 | importFrom(lubridate,dmilliseconds) 40 | importFrom(lubridate,duration) 41 | importFrom(lubridate,wday) 42 | importFrom(lubridate,ymd) 43 | importFrom(lubridate,ymd_hm) 44 | -------------------------------------------------------------------------------- /R/str_hist_with_playlists_wide.R: -------------------------------------------------------------------------------- 1 | #' Prepares the streaming history with playlists data table 2 | #' 3 | #' 4 | #' 5 | #' @param playlists_dt A data table containing the names of playlists and songs on them from spotify. 6 | #' @param streaming_history A data table containing streaming history from spotify. 7 | #' @return A data table containing streaming history from spotify with information on which 8 | #' playlists song is. Data table is in wide format. 9 | #' @export 10 | #' 11 | #' @import data.table 12 | 13 | 14 | 15 | str_his_with_playlists_wide <- function(playlists_dt, streaming_history) { 16 | 17 | str_his_comp <- copy(streaming_history) 18 | 19 | track_name <- artist_name <- in_any <- NULL 20 | setkey(str_his_comp, track_name, artist_name) 21 | setkey(playlists_dt, track_name, artist_name) 22 | 23 | 24 | str_hist <- playlists_dt[str_his_comp] 25 | full <- unique(str_hist) 26 | 27 | wide <- 28 | dcast( 29 | full, 30 | end_time + artist_name + track_name + s_played + start_time + skipped + weekday ~ playlist_name.playlists.name, 31 | value.var = "playlist_name.playlists.name" 32 | ) 33 | 34 | wide[is.na(wide)] <- 0 35 | wide[, ("NA"):= NULL] 36 | wide[, 8: ncol(wide) := lapply(.SD, function(x) {!(x == 0)}), .SDcols = 8 : ncol(wide) 37 | ][,in_any := (do.call(pmax,.SD) > 0), .SDcols = 8:ncol(wide)] 38 | wide 39 | } 40 | -------------------------------------------------------------------------------- /R/most_skipped.R: -------------------------------------------------------------------------------- 1 | #' Most skipped tracks/artist 2 | #' 3 | #' Shows most frequently skipped artist or track in given time period and counts how many times were they skipped 4 | #' 5 | #' 6 | #' @param streaming_history A data.table containing streaming history, 7 | #' 8 | #' after 'prepare_streaming_history' was used on it. 9 | #' @param track_or_artist A character string that specifies whether 10 | #' to show most frequently skipped artists or tracks. 11 | #' Must be either "track" or "artist" 12 | #' @param how_many A positive integer indicating how many most skipped 13 | #' tracks/artist to show, defaults to 10. 14 | #' @param for_view A logical scalar. If \code{TRUE} (default) returns a 15 | #' data.table that is easy to read for humans,with spaces in column names and capitalized first letters 16 | #' 17 | #' 18 | #' 19 | #' @export 20 | #' 21 | #' @import data.table 22 | 23 | most_skipped <- function(streaming_history,track_or_artist,how_many = 10, for_view = TRUE){ 24 | 25 | N <- skipped <- NULL 26 | 27 | most_skipped_dt <- streaming_history[skipped == TRUE, .N, 28 | by = eval(paste(track_or_artist,"_name", sep = "")) 29 | ][order(-N)][order(-N)][1:how_many,] 30 | if (for_view) { 31 | setnames(most_skipped_dt, c(paste(capitalize(track_or_artist), "name"), "Times skipped")) 32 | } 33 | most_skipped_dt 34 | 35 | } -------------------------------------------------------------------------------- /R/plot_day_comparison.R: -------------------------------------------------------------------------------- 1 | 2 | #' Compare two users day play time 3 | #' 4 | #' Compare on which times two users were listening to spotify during a specified day. 5 | #' 6 | #' @param two_users_dt A data table with two users play times on a specific day, made with \code{\link{make_two_users_dt}} 7 | #' @param user1_name A character vector, name to display for the first user. 8 | #' @param user2_name A character vector, name to display for the second user. 9 | #' 10 | #' @export 11 | plot_day_comparison <- function(two_users_dt, user1_name = "User 1", user2_name = "User 2") { 12 | end_time <- start_time <- user <- NULL 13 | breaks <- lubridate::pretty_dates(two_users_dt$end_time, 10) 14 | 15 | viz <- ggplot(two_users_dt) + 16 | geom_rect(aes( 17 | xmin = start_time, 18 | xmax = end_time, 19 | ymin = user - 0.5, 20 | ymax = user + 0.5 , 21 | fill = as.factor(user) 22 | )) + 23 | scale_x_datetime(breaks = breaks, 24 | date_labels = "%H:%M", 25 | guide = guide_axis(n.dodge = 2)) + 26 | scale_y_continuous( 27 | limits = c(-0.1, 3), 28 | breaks = c(1, 2), 29 | minor_breaks = NULL, 30 | labels = c(user1_name, user2_name) 31 | ) + 32 | labs(x = "Hour") + 33 | scale_fill_manual(values = c("#440154FF" , "#7AD151FF")) + 34 | theme_spotifyviz() + 35 | theme(legend.position = "None") 36 | 37 | viz 38 | } 39 | 40 | 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpotifyViz 2 | 3 | 4 | [![Travis build status](https://travis-ci.org/StatsIMUWr/SpotifyViz.svg?branch=master)](https://travis-ci.org/StatsIMUWr/SpotifyViz) 5 | 6 | 7 | An R tool and Shiny application to explore music tastes based on Spotify data. 8 | 9 | [SpotifyViz app](https://asimon.shinyapps.io/SpotifyViz/) 10 | 11 | 12 | Main functionalities: 13 | - data import (from Spotify's json files), 14 | 15 | - cleaning data and extracting implicit data (e.g. weekdays) 16 | 17 | - filtering and summarizing data 18 | - most played artists in given time period 19 | - what percentage of music played is from your playlists in given time period 20 | - how many songs were skipped and which were skipped the most in given time period 21 | - percentage of time spent listening to spotify in given time period 22 | - usage of different devices for spotify (based on search history data) 23 | - usage of spotify in different countries (based on search history data) 24 | - popularity of different playlists 25 | - visualizing hourly/daily lstening habits 26 | - the comparison of listesting habits on one day for two uses 27 | 28 | Made during the "Software development in R course". [See the course repo.](https://github.com/StatsIMUWr/SoftwareDev_R) 29 | 30 | Consulted with Mateusz Staniak. 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /R/compare_history.R: -------------------------------------------------------------------------------- 1 | #' Common artists / artists and tracks for two users. 2 | #' 3 | #' Shows the same artists/ artists and tracks from history of two different users. 4 | #' 5 | #' @param streaming_history_1 A data.table containing streaming history, 6 | #' after 'prepare_streaming_history' was used on it from user 1. 7 | #' @param streaming_history_2 A data.table containing streaming history, 8 | #' after 'prepare_streaming_history' was used on it from user 2. 9 | #' @param by_track Logical value, whether comparison should include tracks. Default is FALSE. 10 | #' 11 | #' @export 12 | #' 13 | #' @import data.table 14 | 15 | 16 | compare_history <- function(streaming_history_1, streaming_history_2, by_track = F) { 17 | 18 | skipped <- . <- track_name <- artist_name <- NULL 19 | str_h_1 <- streaming_history_1[, .(artist_name, track_name, skipped)] 20 | str_h_2 <- streaming_history_2[, .(artist_name, track_name, skipped)] 21 | 22 | if (by_track) { 23 | str_h_1 <- unique(str_h_1[skipped == F, .(track_name, artist_name)]) 24 | str_h_2 <- unique(str_h_2[skipped == F, .(track_name, artist_name)]) 25 | dt <- str_h_1[str_h_2, nomatch = 0, on = c("artist_name", "track_name")] 26 | } 27 | 28 | else { 29 | str_h_1 <- unique(str_h_1[skipped == F, .(artist_name)]) 30 | str_h_2 <- unique(str_h_2[skipped == F, .(artist_name)]) 31 | dt <- str_h_1[str_h_2, nomatch = 0, on = c("artist_name")] 32 | } 33 | 34 | dt 35 | } 36 | -------------------------------------------------------------------------------- /R/utility_functions.R: -------------------------------------------------------------------------------- 1 | #' Capitalize string 2 | #' 3 | #' Capitalize first letter in the string. 4 | #' 5 | #' @param string A string that you want to capitalize. 6 | #' 7 | capitalize <- function(string) { 8 | paste(toupper(substr(string, 1, 1)), 9 | substr(string, 2, nchar(string)), 10 | sep = "") 11 | } 12 | 13 | 14 | #' Change format to hours minutes seconds 15 | #' 16 | #' Change format form number of seconds to number of hours, minutes and seconds. 17 | #' 18 | #' @param x An integer vector. Number of seconds. 19 | #' 20 | #' @return A character vector with format "X h Y min Z s", where Y < 60 and Z < 60 21 | #' 22 | #' @export 23 | #' 24 | #' @examples 25 | #' from_sec_to_hms(c(60, 134, 1257)) 26 | #' 27 | from_sec_to_hms <- function(x) { 28 | times <- sapply(x, from_sec_to_hms_single) 29 | times 30 | } 31 | 32 | #' from_sec_to_hms helper function. 33 | #' 34 | #' Function to be applied to an integer, representing number of seconds, 35 | #' changes the format to "X h Y min Z s". Applied to each element of the vector by 36 | #' \code{from_sec_to_hms}. 37 | #' @param x An integer vector. Number of seconds. 38 | #' 39 | from_sec_to_hms_single <- function(x) { 40 | hours <- floor(x / 3600) 41 | mins <- floor((x - 3600 * hours) / 60) 42 | secs <- x - mins * 60 - hours * 3600 43 | time <- "" 44 | if (hours > 0) time <- paste(time, hours, "h") 45 | if (mins > 0) time <- paste(time, mins, "min") 46 | if (secs > 0) time <- paste(time, secs, "s") 47 | if (time == "") time <- "0s" 48 | time 49 | } -------------------------------------------------------------------------------- /R/most_played.R: -------------------------------------------------------------------------------- 1 | #' Most played tracks/artist 2 | #' 3 | #' Shows most frequently played artist or track and counts how many times were they played. 4 | #' 5 | #' 6 | #' @param streaming_history A data.table containing streaming history, after 'prepare_streaming_history' was used on it. 7 | #' @param track_or_artist A character string that specifies whether to show most frequently played artists or tracks. 8 | #' Must be either "track" or "artist" 9 | #' @param how_many A positive integer indicating how many most frequent tracks/artist to show, defaults to 10. 10 | #' @param show_skipped A logic scalar indicating whether to include tracks that were skipped in counting. 11 | #' 12 | #' 13 | #' @export 14 | #' 15 | #' @import data.table 16 | 17 | 18 | most_played <- function(streaming_history, 19 | track_or_artist, 20 | how_many = 10, 21 | show_skipped = TRUE) { 22 | N <- skipped <- NULL 23 | 24 | if (show_skipped) { 25 | most_played_dt <- streaming_history[, .N, 26 | by = eval(paste(track_or_artist, "_name", sep = ""))][order(-N)][1:how_many, ] 27 | } 28 | else { 29 | most_played_dt <- streaming_history[skipped == FALSE, .N, 30 | by = eval(paste(track_or_artist, "_name", sep = ""))][order(-N)][order(-N)][1:how_many, ] 31 | } 32 | 33 | setnames(most_played_dt, c(paste(capitalize(track_or_artist), "name"), "Times played")) 34 | 35 | most_played_dt 36 | } 37 | -------------------------------------------------------------------------------- /man/measure_how_long_listened.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/measure_how_long_listened.R 3 | \name{measure_how_long_listened} 4 | \alias{measure_how_long_listened} 5 | \title{Measure time spent listening to spotify} 6 | \usage{ 7 | measure_how_long_listened( 8 | streaming_history, 9 | start_date, 10 | end_date, 11 | as_percentage = FALSE, 12 | for_viewing = TRUE 13 | ) 14 | } 15 | \arguments{ 16 | \item{streaming_history}{A data.table containing streaming history, after 17 | 'prepare_streaming_history' was used on it} 18 | 19 | \item{start_date}{A POSIXt,Date or string that can be coerced into Date 20 | by \code{\link{as_date}} indicating start of the period of time to count tracks} 21 | 22 | \item{end_date}{A POSIXt,Date or string that can be coerced into Date 23 | by \code{\link{as_date}} indicating end of the period of time to count tracks} 24 | 25 | \item{as_percentage}{A logical scalar. If \code{FALSE} (default) length of time 26 | (in seconds and an approximate in biggest reasonable unit) 27 | that songs were listened to in given time period is returned, 28 | otherwise a character vector indicating percentage of given time period will be returned.} 29 | 30 | \item{for_viewing}{A logical scalar} 31 | } 32 | \value{ 33 | An integer if \code{as_percentage} is \code{FALSE} and a character vector 34 | if \code{as_percentage} is \code{TRUE} 35 | } 36 | \description{ 37 | Measures time spent listening to spotify during given time period. 38 | Either as a duration or as a percentage of the whole given period of time. 39 | } 40 | -------------------------------------------------------------------------------- /R/prepare_streaming_history.R: -------------------------------------------------------------------------------- 1 | #' Prepares the streaming history data table 2 | #' 3 | #' Prepares the data table containing streaming history for further operations. 4 | #' Changes the names of the base columns to: end_time, artist_name, track_name, s_played. 5 | #' Changes the end_time 6 | #' column from character to POSIXt date, s_played to a duration in seconds. Adds new columns: 7 | #' start_time - estimated start time calculated from end_time and s_played, skipped - 8 | #' TRUE if the track was 9 | #' played for less than 10s, weekday - weekdays derived from start_time date. \cr 10 | #' 11 | #' 12 | #' @param streaming_history A raw data table containing streaming history from spotify. 13 | #' 14 | #' @return A data table containing streaming history from spotify suited for being used 15 | #' in rest of the functions from the package. 16 | #' 17 | #' @export 18 | #' 19 | #' @import data.table 20 | #' @importFrom lubridate dmilliseconds wday ymd_hm duration 21 | #' 22 | 23 | prepare_streaming_history <- function(streaming_history){ 24 | 25 | end_time <- s_played <- weekday <- start_time <- NULL 26 | streaming_history <- copy(streaming_history) 27 | setnames(streaming_history,c("end_time", "artist_name", "track_name", "s_played")) 28 | 29 | streaming_history[,`:=`(end_time = ymd_hm(end_time), s_played = dmilliseconds((s_played))) 30 | ][,`:=`(start_time = end_time - s_played, 31 | skipped = (s_played < duration(10, "seconds"))) 32 | ][,weekday := lubridate::wday(start_time, label = TRUE)] 33 | } 34 | 35 | -------------------------------------------------------------------------------- /R/count_skipped.R: -------------------------------------------------------------------------------- 1 | #' Count skipped songs in given time period 2 | #' 3 | #' Counts how many songs were skipped in given time period. Returns either a number or a percentage of all songs 4 | #' in given time period depending on user 5 | #' 6 | #' @param streaming_history data.table containing streaming history, 7 | #' after 'prepare_streaming_history' was used on it 8 | #' 9 | #' @param start_date A POSIXt,Date or string that can be coerced into Date 10 | #' by \code{\link{as_date}} indicating start of the period of time to count skipped songs 11 | #' 12 | #' @param end_date A POSIXt,Date or string that can be coerced into Date 13 | #' by \code{\link{as_date}} indicating end of the period of time to count skipped songs 14 | #' 15 | #' @param as_percentage logical. if FALSE (default) a number of skipped songs is returned, 16 | #' otherwise a character vector indicating percentage of skipped songs in given period 17 | #' 18 | #' @export 19 | #' 20 | #' @import data.table 21 | #' 22 | count_skipped <- 23 | function(streaming_history, 24 | start_date, 25 | end_date, 26 | as_percentage = FALSE) { 27 | start_time <- end_time <- skipped <- NULL 28 | end_date <- as_date(end_date) 29 | start_date <- as_date(start_date) 30 | data <- streaming_history[start_date <= start_time & 31 | as_date(end_date) + duration(86399, "second") >= end_time, ] 32 | n <- data[skipped == TRUE, .N] 33 | if (as_percentage) { 34 | return(paste(round((n / data[, .N]) * 100, digits = 2), "%", sep = "")) 35 | } 36 | return(n) 37 | } 38 | -------------------------------------------------------------------------------- /R/sessions_visualize.R: -------------------------------------------------------------------------------- 1 | #' Visualize sessions length 2 | #' 3 | #' Visualize sessions length divided on intervals using bar plot. 4 | #' 5 | #' 6 | #' @param streaming_history A data.table containing streaming history, after 7 | #' 'prepare_streaming_history' was used on it. 8 | #' @param mins Number of minutes which determine distance between listenings. 9 | #' @param as_percentage A logical value. If TRUE (default) bars show percentage of of all sessions. 10 | #' @export 11 | #' 12 | #' @import data.table 13 | #' @import ggplot2 14 | #' 15 | 16 | 17 | sessions_visualize <- function(streaming_history, mins, as_percentage = TRUE) { 18 | 19 | frac <- NULL 20 | 21 | sessions_groupped <- sessions_intervals(streaming_history, mins) 22 | 23 | 24 | if (as_percentage) { 25 | viz <- ggplot(sessions_groupped, aes(x = interval, y = frac / 100)) + 26 | geom_bar(stat = "identity", position = "dodge", fill = "#440154FF", colour = "white") + 27 | geom_text(aes(label = frac), position = position_dodge(width = 0.9), vjust = -0.5) + 28 | scale_y_continuous(labels = function(x) paste(x*100, "%")) + 29 | labs(x = "Session length", y = "Percentage of all tracks played") + 30 | theme_spotifyviz() 31 | } else { 32 | viz <- ggplot(sessions_groupped, aes(x = interval, y = sum)) + 33 | geom_bar( stat = "identity", position = "dodge", fill = "#440154FF", colour = "white") + 34 | geom_text(aes(label = sum), position = position_dodge(width = 0.9), vjust = -0.5) + 35 | labs(x = "Session length", y = "Sessions") + 36 | theme_spotifyviz() 37 | } 38 | 39 | viz 40 | } 41 | -------------------------------------------------------------------------------- /R/make_two_users_dt.R: -------------------------------------------------------------------------------- 1 | #' Make data table to use in \code{plot_day_comparison} 2 | #' 3 | #' Make data table with data about play time of two users on given day, 4 | #' for further use in \code{\link{plot_day_comparison}} 5 | #' 6 | #' @param user1_data A data.table containing streaming history from first user, 7 | #' made with \code{\link{complete_streaming_history}}. 8 | #' 9 | #' @param user2_data A data.table containing streaming history from second user, 10 | #' made with \code{\link{complete_streaming_history}}. 11 | #' 12 | #' @param day end_date A POSIXt, Date or string that can be coerced into 13 | #' Date by \code{\link{as_date}} indicating the day for witch to create the data table. 14 | #' 15 | #' @export 16 | 17 | make_two_users_dt <- function(user1_data, user2_data, day) { 18 | . <- end_time <- start_time <- NULL 19 | 20 | user1_data <- user1_data[as_date(day) <= start_time 21 | & 22 | as_date(day) + duration(86399, "second") >= end_time, 23 | .(start_time, end_time)][, `:=`( 24 | start_time = floor_date(start_time, "minutes"), 25 | end_time = ceiling_date(end_time, "minutes"), 26 | user = 1 27 | )] 28 | user2_data <- user2_data[as_date(day) <= start_time 29 | & 30 | as_date(day) + duration(86399, "second") >= end_time, 31 | .(start_time, end_time)][, `:=`( 32 | start_time = floor_date(start_time, "minutes"), 33 | end_time = ceiling_date(end_time, "minutes"), 34 | user = 2 35 | )] 36 | rbindlist(list(user1_data, user2_data)) 37 | } 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /R/make_summary_dt.R: -------------------------------------------------------------------------------- 1 | #' Summarize streaming history 2 | #' 3 | #' Make table with summary stats about streaming history in given data period. 4 | #' 5 | #' @param streaming_history A data.table containing streaming history, after 6 | #' 'prepare_streaming_history' was used on it. 7 | #' 8 | #' @param start_date A POSIXt,Date or string that can be coerced into 9 | #' Date by \code{\link{as_date}} indicating start of the period of time. 10 | #' 11 | #' @param end_date A POSIXt,Date or string that can be coerced into 12 | #' Date by \code{\link{as_date}} indicating end of the period of time. 13 | #' 14 | #' @param as_percentage A logical scalar. If \code{TRUE} (default) 15 | #' tracks skipped and time listened to will be a percentage of all tracks and whole time period. 16 | #' 17 | #' 18 | #' @return A data table with summarized streaming history 19 | #' @export 20 | 21 | 22 | 23 | 24 | make_summary_dt <- function(streaming_history, 25 | start_date, 26 | end_date, 27 | as_percentage = TRUE) { 28 | . <- track_name <- artist_name <- NULL 29 | filtered <- filter_streaming_history(streaming_history, start_date, end_date) 30 | hm_tracks <- filtered[, .N] 31 | hm_different_tracks <- uniqueN(filtered[, .(track_name)]) 32 | hm_skipped <- count_skipped(streaming_history, start_date, end_date, as_percentage) 33 | hm_different_artists <- uniqueN(filtered[, .(artist_name)]) 34 | hl_listened <- measure_how_long_listened(streaming_history, start_date, end_date, as_percentage) 35 | 36 | summary <- data.table( 37 | `Tracks played` = hm_tracks, 38 | `Different tracks` = hm_different_tracks, 39 | `Different artists` = hm_different_artists, 40 | `Skipped tracks` = hm_skipped, 41 | `How long listened` = hl_listened 42 | ) 43 | summary 44 | } 45 | -------------------------------------------------------------------------------- /R/continuous_listening.R: -------------------------------------------------------------------------------- 1 | #' Divides streaming history into a groups of song which where listened one after another. 2 | #' 3 | #' The listening number indicates the session number. 4 | #' If distance between beginning of a song and end of a previous one is less than 5 | #' \code{mins} then listening number stays. 6 | #' Otherwise increases. Counting begins from the oldest song in streaming history. 7 | #' 8 | #' @param streaming_history A data table containing streaming history from spotify. 9 | #' @param mins Number of minutes which determine distance between listening sessions. 10 | #' @return A data table containing streaming history with additional column about listening number. 11 | #' @export 12 | #' 13 | #' @import data.table 14 | 15 | 16 | 17 | continuous_listening <- function(streaming_history, mins) { 18 | skipped <- end_time <- artist_name <- track_name <- s_played <- start_time <- NULL 19 | weekday <- end_of_prev <- diff <- . <- NULL 20 | streaming_history <- streaming_history[order(start_time, end_time)] 21 | streaming_history <- streaming_history[, .(end_time, artist_name, track_name, s_played, 22 | start_time, skipped, weekday, 23 | end_of_prev = data.table::shift(end_time, 24 | fill = min(end_time)))] 25 | 26 | streaming_history <- streaming_history[, .(end_time, artist_name, track_name, s_played, 27 | start_time, skipped, weekday, 28 | diff = as.numeric(start_time - end_of_prev))] 29 | 30 | 31 | streaming_history <- streaming_history[, .(end_time, artist_name, track_name, s_played, 32 | start_time, skipped, weekday, 33 | listening_number = (cumsum(diff > mins * 60)) + 1)] 34 | 35 | streaming_history 36 | } 37 | -------------------------------------------------------------------------------- /R/measure_how_long_listened.R: -------------------------------------------------------------------------------- 1 | #' Measure time spent listening to spotify 2 | #' 3 | #' Measures time spent listening to spotify during given time period. 4 | #' Either as a duration or as a percentage of the whole given period of time. 5 | #' 6 | #' @param streaming_history A data.table containing streaming history, after 7 | #' 'prepare_streaming_history' was used on it 8 | #' 9 | #' @param start_date A POSIXt,Date or string that can be coerced into Date 10 | #' by \code{\link{as_date}} indicating start of the period of time to count tracks 11 | #' 12 | #' @param end_date A POSIXt,Date or string that can be coerced into Date 13 | #' by \code{\link{as_date}} indicating end of the period of time to count tracks 14 | #' 15 | #' @param as_percentage A logical scalar. If \code{FALSE} (default) length of time 16 | #' (in seconds and an approximate in biggest reasonable unit) 17 | #' that songs were listened to in given time period is returned, 18 | #' otherwise a character vector indicating percentage of given time period will be returned. 19 | #' 20 | #' @param for_viewing A logical scalar 21 | #' 22 | #' @return An integer if \code{as_percentage} is \code{FALSE} and a character vector 23 | #' if \code{as_percentage} is \code{TRUE} 24 | #' 25 | #' @export 26 | #' 27 | #' @import data.table 28 | #' @import lubridate 29 | 30 | measure_how_long_listened <- function(streaming_history, start_date, end_date, as_percentage = FALSE, 31 | for_viewing = TRUE) { 32 | start_time <- s_played <- NULL 33 | end_date <- as_date(end_date) 34 | start_date <- as_date(start_date) 35 | total <- round(streaming_history[start_time >= start_date & start_time <= as_date(end_date) + duration(86399, "second"), sum(s_played)], 0) 36 | seconds_in_period <- as.numeric(difftime(end_date, start_date, units = "secs")) 37 | percent <- round(total / seconds_in_period * 100, digits = 2) 38 | if (as_percentage & for_viewing) return(paste(percent, "%", sep = "")) 39 | if (as_percentage) return(percent) 40 | if (for_viewing) return(from_sec_to_hms(total)) 41 | total 42 | } 43 | -------------------------------------------------------------------------------- /R/plot_track_count.R: -------------------------------------------------------------------------------- 1 | #' visualize number of tracks that were played or skipped 2 | #' 3 | #' visualizes, using a bar chart, how many tracks were played or how many were skipped, each period (day, week, month, year). 4 | #' 5 | #' @param streaming_history A data.table containing streaming history, 6 | #' after 'prepare_streaming_history' was used on it. 7 | #' @param only_skipped A logical scalar indicating whether to show 8 | #' all played tracks or only those that were skipped. 9 | #' @param by A character vector indicating for which periods 10 | #' to split the time for counting tracks played/skipped. 11 | #' Permitted values are: "year", "month", "week", "day". 12 | #' 13 | #' @export 14 | #' 15 | #' @import data.table 16 | #' @import lubridate 17 | 18 | 19 | plot_track_count <- 20 | function(streaming_history, 21 | only_skipped = FALSE, 22 | by = "year") { 23 | skipped <- end_time <- ..count.. <- . <- NULL 24 | 25 | 26 | pretty_breaks <- 27 | as.Date(lubridate::pretty_dates((streaming_history$end_time), 5)) 28 | 29 | 30 | if (only_skipped) { 31 | streaming_history <- streaming_history[(skipped), ] 32 | y_lab = "Number of tracks skipped" 33 | } 34 | else { 35 | streaming_history <- 36 | streaming_history[, .(skipped = factor( 37 | skipped, 38 | levels = c("TRUE", "FALSE"), 39 | labels = c("Yes", "No")), 40 | end_time = end_time)] 41 | y_lab = "Number of tracks played" 42 | } 43 | viz <- 44 | ggplot(streaming_history, aes(x = as.Date(floor_date(end_time, by)))) + 45 | labs(y = y_lab, x = "Date") + 46 | theme_spotifyviz() + 47 | theme(legend.position = "bottom") 48 | if (only_skipped) { 49 | viz <- 50 | viz + geom_bar(aes(y = ..count..), fill = "#440154FF", colour = "white") 51 | } 52 | else{ 53 | viz <- viz + geom_bar(aes(y = ..count.., fill = skipped)) + 54 | scale_fill_manual(name = "Was the track skipped", 55 | values = c("#440154FF" , "#7AD151FF")) 56 | } 57 | viz <- switch( 58 | by, 59 | "year" = viz + scale_x_date(date_labels = "%Y", date_breaks = ("1 year")), 60 | "month" = viz + scale_x_date(date_labels = "%m.%y", date_breaks = "2 months"), 61 | "week" = viz + scale_x_date(date_labels = "%m.%y", breaks = pretty_breaks), 62 | "day" = viz + scale_x_date(date_labels = "%d.%m.%y", breaks = pretty_breaks) 63 | ) 64 | viz 65 | } 66 | -------------------------------------------------------------------------------- /R/plot_playlist_popularity.R: -------------------------------------------------------------------------------- 1 | #' Visualize popularity of different playlist 2 | #' 3 | #' Visualize, using a bar chart, how popular were different playlists. Either by number of tracks played or play time. 4 | #' 5 | #' @param str_his_with_playlists_long A data table containing streaming history with information about playlists, 6 | #' in long form, made with \code{\link{str_his_with_playlists_long}}. 7 | #' @param time_or_count A character vector indicating whether to count number of tracks played 8 | #' or the time they were played. 9 | #' @param time_unit A character vector indicating the time unit to use if showing an absolute value of time played. 10 | #' Permitted vales are "seconds", "minutes", "hours". 11 | #' 12 | #' @export 13 | 14 | 15 | plot_playlist_popularity <- 16 | function(str_his_with_playlists_long, 17 | time_or_count = "time", 18 | time_unit = "hours") { 19 | . <- playlist_name <- s_played <- time <- count <- NULL 20 | 21 | 22 | summary <- 23 | str_his_with_playlists_long[, .(count = .N, time = sum(s_played)), by = playlist_name][(!is.na(playlist_name)), ][order(eval(as.name(time_or_count)))][, playlist_name := factor(playlist_name, 24 | levels = as.character(playlist_name))] 25 | viz <- ggplot(summary) + 26 | theme_spotifyviz() + 27 | theme(axis.title.y = element_blank()) 28 | 29 | if (time_or_count == "time") { 30 | time_units <- c("seconds" = 1, 31 | "minutes" = 60, 32 | "hours" = 3600) 33 | abbr <- c("seconds" = "s", 34 | "minutes" = "min", 35 | "hours" = "h") 36 | 37 | viz <- viz + 38 | geom_bar( 39 | aes(y = playlist_name, x = time / time_units[time_unit]), 40 | stat = "identity", 41 | fill = "#440154FF", 42 | colour = "white" 43 | ) + 44 | labs(x = "Play time") + 45 | scale_x_continuous( 46 | labels = function(x) { 47 | paste(x, abbr[time_unit]) 48 | } 49 | ) 50 | 51 | 52 | } 53 | 54 | else{ 55 | viz <- viz + 56 | geom_bar( 57 | aes(y = playlist_name, x = count), 58 | stat = "identity", 59 | fill = "#440154FF", 60 | colour = "white" 61 | ) + 62 | labs(x = "Tracks played") 63 | 64 | } 65 | 66 | viz 67 | } 68 | -------------------------------------------------------------------------------- /R/plot_in_playlists_count.R: -------------------------------------------------------------------------------- 1 | #' Visualize how much of music listened to was from playlists 2 | #' 3 | #' Visualize, using a bar chart, how much music listened to was from playlists. 4 | #' Either counting the number of tracks played or play time. 5 | 6 | #' @param str_his_with_playlist_wide A data table containing streaming history 7 | #' with information about playlists, in wide form, made with \code{\link{str_his_with_playlists_wide}}. 8 | #' 9 | #' @param as_percentage A logical scalar indicating whether to show a percentage or an absolute value. 10 | #' 11 | #' @param time_or_count A character vector indicating whether to count 12 | #' number of tracks played or the time they were played. 13 | #' 14 | #' @param time_unit A character vector indicating the time unit to use 15 | #' if showing an absolute value of time played. Permitted vales are "seconds", "minutes", "hours". 16 | #' 17 | #' @import data.table 18 | #' @import ggplot2 19 | #' 20 | #' 21 | #' @export 22 | 23 | 24 | plot_in_playlists_count <- function(str_his_with_playlist_wide, 25 | as_percentage = FALSE, 26 | time_or_count = "time", 27 | time_unit = "minutes") { 28 | . <- played <- in_any <- s_played <- NULL 29 | 30 | abbr <- c("seconds" = "s", "minutes" = "min", "hours" = "h") 31 | 32 | if (time_or_count == "time") { 33 | time_units <- c("seconds" = 1, 34 | "minutes" = 60, 35 | "hours" = 3600) 36 | time_dt <-str_his_with_playlist_wide[, .(played = sum(s_played) / time_units[[time_unit]]), 37 | by = in_any] 38 | y_label <- "Play time" 39 | y_label_percent <- "Percentage of all play time" 40 | viz <- ggplot(time_dt) + 41 | theme_spotifyviz() 42 | } 43 | 44 | else{ 45 | count_dt <- str_his_with_playlist_wide[, .(played = .N), by = in_any] 46 | viz <- ggplot(count_dt) + 47 | theme_spotifyviz() 48 | y_label <- "Tracks played" 49 | y_label_percent <- "Percentage of all tracks played" 50 | } 51 | 52 | 53 | if (!as_percentage) { 54 | viz <- viz + 55 | geom_bar( 56 | aes(x = in_any, y = played), 57 | stat = "identity", 58 | fill = "#440154FF", 59 | colour = "white" 60 | ) + 61 | labs(y = y_label) 62 | if (time_or_count == "time") { 63 | viz <- 64 | viz + scale_y_continuous( 65 | labels = function(x) 66 | paste(x, abbr[time_unit]) 67 | ) 68 | } 69 | } 70 | 71 | else { 72 | viz <- viz + 73 | geom_bar( 74 | aes(x = in_any, y = played / sum(played)), 75 | stat = "identity", 76 | fill = "#440154FF", 77 | colour = "white" 78 | ) + 79 | scale_y_continuous( 80 | labels = function(x) 81 | paste(x * 100, "%") 82 | ) + 83 | labs(y = y_label_percent) 84 | } 85 | 86 | viz <- viz + 87 | scale_x_discrete(labels = c("Other tracks","Tracks from playlists"), name = NULL) 88 | 89 | viz 90 | } 91 | -------------------------------------------------------------------------------- /R/plot_track_count_by_period.R: -------------------------------------------------------------------------------- 1 | #' Visualize number of tracks played by period 2 | #' 3 | #' Visualizes, using a bar chart, the number or percentage of songs that were played on different hours/weekdays. 4 | #' If split by hours, can be also faceted by weekday. 5 | #' 6 | #' 7 | #' @param filtered A data.table containing streaming history, after 'prepare_streaming_history' 8 | #' was used on it. 9 | #' @param period A character vector indicating for which periods to split the time for 10 | #' counting tracks played. 11 | #' Permitted values are: "weekday", "hour" 12 | #' @param as_percentage A logical scalar. If TRUE (default) bars show percentage 13 | #' of all tracks played for each weekday. 14 | #' @param include_skipped A logical scalar indicating whether to include tracks that were skipped 15 | #' @param by_weekday A logical scalar indicating whether to split the graph by weekdays 16 | #' if period is "hour" 17 | 18 | #' @export 19 | #' 20 | #' @import data.table 21 | #' @import ggplot2 22 | #' @import lubridate 23 | #' 24 | 25 | 26 | plot_track_count_by_period <- function(filtered, 27 | period = "weekday", 28 | as_percentage = TRUE, 29 | include_skipped = FALSE, 30 | by_weekday = FALSE) { 31 | skipped <- end_time <- ..count.. <- ..PANEL.. <- NULL 32 | 33 | if (!include_skipped) { 34 | filtered <- filtered[skipped == FALSE,] 35 | } 36 | 37 | functions <- list( 38 | weekday = function(x) { 39 | lubridate::wday(x, 40 | label = TRUE, 41 | abbr = FALSE, 42 | week_start = 1) 43 | }, 44 | hour = lubridate::hour 45 | ) 46 | 47 | viz <- ggplot(filtered, aes(x = functions[[period]](end_time))) + 48 | theme_spotifyviz() 49 | 50 | 51 | if (as_percentage) { 52 | if (by_weekday && period == "hour") { 53 | viz <- viz + 54 | geom_bar(aes(y = (..count..) / tapply(..count.., ..PANEL.., sum)[..PANEL..]), 55 | fill = "#440154FF", 56 | colour = "white") + 57 | facet_wrap( ~ weekday) + 58 | labs(x = "Hour", y = "Percentage (within each weekday) of tracks played ") + 59 | scale_y_continuous( 60 | labels = function(x) 61 | paste(x * 100, "%") 62 | ) 63 | } 64 | else { 65 | viz <- viz + 66 | geom_bar(aes(y = (..count..) / sum(..count..)) , fill = "#440154FF", colour = "white") + 67 | xlab(capitalize(period)) + 68 | ylab("Percentage of all tracks played") + 69 | scale_y_continuous( 70 | labels = function(x) 71 | paste(x * 100, "%") 72 | ) 73 | } 74 | } 75 | else{ 76 | viz <- viz + 77 | geom_bar(fill = "#440154FF", colour = "white") + 78 | xlab(capitalize(period)) + 79 | ylab("Tracks played") 80 | 81 | if (by_weekday && period == "hour") { 82 | viz <- viz + 83 | facet_wrap( ~ weekday) 84 | } 85 | } 86 | 87 | 88 | viz 89 | } 90 | 91 | 92 | -------------------------------------------------------------------------------- /tests/testthat/test-getting_info_functions.R: -------------------------------------------------------------------------------- 1 | context("Getting info functions") 2 | 3 | testthat::test_that( 4 | "measure_how_long_listened function test: check types of data, four values and containing of % sign", 5 | { 6 | a <- measure_how_long_listened(spotifyviz::clean_stream_his, "18.11.04", "18.11.07") 7 | expect_equal(a, " 5 h 13 min 52 s") 8 | expect_is(a, "character") 9 | 10 | a <- measure_how_long_listened(spotifyviz::clean_stream_his, 11 | "18.11.04", 12 | "18.11.07", 13 | as_percentage = TRUE) 14 | expect_equal(a, "7.27%") 15 | expect_is(a, "character") 16 | expect_match(a, "%") 17 | 18 | a <- measure_how_long_listened(spotifyviz::clean_stream_his, 19 | "18.11.04", 20 | "18.11.07", 21 | for_viewing = FALSE) 22 | expect_equal(a, 18832) 23 | expect_is(a, "numeric") 24 | 25 | a <- measure_how_long_listened( 26 | spotifyviz::clean_stream_his, 27 | "18.11.04", 28 | "18.11.07", 29 | as_percentage = TRUE, 30 | for_viewing = FALSE 31 | ) 32 | expect_equal(a, 7.27) 33 | expect_is(a, "numeric") 34 | } 35 | ) 36 | 37 | testthat::test_that( 38 | "count_skipped function test:: check types of data, two values and containing of % sign", 39 | { 40 | a <- count_skipped(spotifyviz::clean_stream_his, "18.11.04", "18.11.07") 41 | expect_equal(a, 22) 42 | expect_is(a, "integer") 43 | a <- count_skipped(spotifyviz::clean_stream_his, 44 | "18.11.04", 45 | "18.11.07", 46 | as_percentage = TRUE) 47 | expect_equal(a, "22%") 48 | expect_is(a, "character") 49 | expect_match(a, "%") 50 | 51 | } 52 | ) 53 | 54 | testthat::test_that("longest_session function test: checks dimensions and number of sessions", { 55 | p <- longest_session(spotifyviz::clean_stream_his, 10) 56 | expect_equal(dim(p), c(27, 8)) 57 | expect_equal(length(unique(p[[8]])), 1) 58 | 59 | }) 60 | 61 | 62 | testthat::test_that("most_played function test: checks four values, class of columns and dimensions", { 63 | a <- most_played(spotifyviz::clean_stream_his, "track", 10, TRUE) 64 | b <- most_played(spotifyviz::clean_stream_his, "artist", 10, TRUE) 65 | c <- most_played(spotifyviz::clean_stream_his, "track", 10, FALSE) 66 | d <- most_played(spotifyviz::clean_stream_his, "artist", 10, FALSE) 67 | expect_equal(dim(a), dim(b), c(10, 2)) 68 | expect_equal(dim(c), dim(d), c(10, 2)) 69 | expect_equal(class(a[[1]]), class(b[[1]]), class(c[[1]]), class(d[[1]]), "character") 70 | expect_equal(class(a[[2]]), class(b[[2]]), class(c[[2]]), class(d[[2]]), "integer") 71 | expect_equal(a[[1]][1], "Firth Of Fifth - Remastered 2008") 72 | expect_equal(b[[1]][1], "Genesis") 73 | expect_equal(c[[1]][1], "Dancing With The Moonlit Knight - Remastered 2008") 74 | expect_equal(d[[1]][1], "Rise Against") 75 | }) 76 | 77 | testthat::test_that("most_skipped function test: checks four values, class of columns, dimensions and column names", { 78 | a <- most_skipped(spotifyviz::clean_stream_his, "track", 10, TRUE) 79 | b <- most_skipped(spotifyviz::clean_stream_his, "artist", 10, TRUE) 80 | c <- most_skipped(spotifyviz::clean_stream_his, "track", 10, FALSE) 81 | d <- most_skipped(spotifyviz::clean_stream_his, "artist", 10, FALSE) 82 | expect_equal(dim(a), dim(b), c(10, 2)) 83 | expect_equal(dim(c), dim(d), c(10, 2)) 84 | expect_equal(class(a[[1]]), class(b[[1]]), class(c[[1]]), class(d[[1]]), "character") 85 | expect_equal(class(a[[2]]), class(b[[2]]), class(c[[2]]), class(d[[2]]), "integer") 86 | expect_equal(a[[1]][1], "Firth Of Fifth - Remastered 2008") 87 | expect_equal(b[[1]][1], "Genesis") 88 | expect_equal(c[[1]][1], "Firth Of Fifth - Remastered 2008") 89 | expect_equal(d[[1]][1], "Genesis") 90 | expect_equal(colnames(a), c("Track name", "Times skipped")) 91 | expect_equal(colnames(b), c("Artist name", "Times skipped")) 92 | expect_equal(colnames(c), c("track_name", "N")) 93 | expect_equal(colnames(d), c("artist_name", "N")) 94 | }) 95 | -------------------------------------------------------------------------------- /tests/testthat/test-preparing_functions.R: -------------------------------------------------------------------------------- 1 | context("Preparing functions") 2 | 3 | 4 | 5 | testthat::test_that("StreamingHistory preparing test: checks dimensions, object type and column types", 6 | { 7 | p <- prepare_streaming_history(spotifyviz::dirty_stream_his) 8 | expect_equal(dim(p), c(100, 7)) 9 | expect_true(is.POSIXt(p[[1]])) 10 | expect_is(p[[2]], "character") 11 | expect_is(p[[3]], "character") 12 | expect_is(p[[4]], "Duration") 13 | expect_true(is.POSIXt(p[[5]])) 14 | expect_is(p[[6]], "logical") 15 | expect_true(is.factor(p[[7]])) 16 | expect_is(p, "data.table") 17 | }) 18 | 19 | testthat::test_that( 20 | "CompleteStreamHis reading and preparing test: checks dimensions, object type and column types", 21 | { 22 | p <- complete_streaming_history("DataFromSpotify") 23 | expect_equal(dim(p), c(100, 7)) 24 | expect_true(is.POSIXt(p[[1]])) 25 | expect_is(p[[2]], "character") 26 | expect_is(p[[3]], "character") 27 | expect_is(p[[4]], "Duration") 28 | expect_true(is.POSIXt(p[[5]])) 29 | expect_is(p[[6]], "logical") 30 | expect_true(is.factor(p[[7]])) 31 | expect_is(p, "data.table") 32 | } 33 | ) 34 | 35 | testthat::test_that( 36 | "Preparing long StreamHisWithPlaylist test :checks dimensions, object type, 37 | column types and one of many data table value", 38 | { 39 | p <- str_his_with_playlists_long(spotifyviz::clean_playlist, 40 | spotifyviz::clean_stream_his) 41 | expect_equal(dim(p), c(100, 8)) 42 | expect_is(p[[1]], "character") 43 | expect_is(p[[2]], "character") 44 | expect_is(p[[3]], "character") 45 | expect_true(is.POSIXt(p[[4]])) 46 | expect_is(p[[5]], "Duration") 47 | expect_true(is.POSIXt(p[[6]])) 48 | expect_is(p[[7]], "logical") 49 | expect_true(is.factor(p[[8]])) 50 | expect_is(p, "data.table") 51 | expect_equal(p[[1]][12], "Urodzinowa playlista") 52 | } 53 | ) 54 | 55 | testthat::test_that("Preparing wide StreamHisWithPlaylist test::checks dimensions, object type, 56 | column types and two of many data table value", { 57 | p <- str_his_with_playlists_wide(spotifyviz::clean_playlist, 58 | spotifyviz::clean_stream_his) 59 | expect_equal(dim(p), c(100, 9)) 60 | expect_true(is.POSIXt(p[[1]])) 61 | expect_is(p[[2]], "character") 62 | expect_is(p[[3]], "character") 63 | expect_is(p[[4]], "Duration") 64 | expect_true(is.POSIXt(p[[5]])) 65 | expect_is(p[[6]], "logical") 66 | expect_true(is.factor(p[[7]])) 67 | expect_is(p[[8]], "logical") 68 | expect_is(p[[9]], "logical") 69 | expect_true(p[[8]][30]) 70 | expect_true(p[[9]][30]) 71 | expect_is(p, "data.table") 72 | }) 73 | 74 | testthat::test_that("make_session_stats function test :checks dimensions and column types" , { 75 | p <- make_session_stats(spotifyviz::clean_stream_his) 76 | expect_equal(dim(p), c(1, 4)) 77 | expect_is(p[[1]], "character") 78 | expect_is(p[[2]], "character") 79 | expect_is(p[[3]], "integer") 80 | expect_is(p[[4]], "list") 81 | }) 82 | 83 | testthat::test_that( 84 | "make_summary_dt function test: :checks dimensions, compare values and containing of % sign", 85 | { 86 | a <- 87 | make_summary_dt(spotifyviz::clean_stream_his, "18.11.05", "18.11.05") 88 | b <- make_summary_dt(spotifyviz::clean_stream_his, 89 | "18.11.03", 90 | "18.11.06", 91 | as_percentage = TRUE) 92 | expect_equal(dim(a), dim(b), c(1, 5)) 93 | expect_true(a[[1]] <= b[[1]]) 94 | expect_true(a[[3]] <= a[[2]] & a[[2]] <= a[[1]]) 95 | expect_true(b[[3]] <= b[[2]] & b[[2]] <= b[[1]]) 96 | expect_match(b[[4]], "%") 97 | } 98 | ) 99 | 100 | testthat::test_that("made_two_users_dt function test: :checks dimensions, number of sessions, 101 | column types", { 102 | p <- make_two_users_dt(spotifyviz::clean_stream_his, 103 | spotifyviz::clean_stream_his, 104 | "18.11.5") 105 | expect_equal(dim(p), c(90, 3)) 106 | expect_equal(length(unique(p[[3]])), 2) 107 | expect_true(is.POSIXt(p[[1]])) 108 | expect_true(is.POSIXt(p[[2]])) 109 | 110 | 111 | }) 112 | 113 | 114 | testthat::test_that( 115 | "sessions_intervals function test: checks dimensions, sums of percents and sum of sessions", 116 | { 117 | p <- sessions_intervals(spotifyviz::clean_stream_his, 10) 118 | q <- sessions_length(spotifyviz::clean_stream_his, 10) 119 | expect_equal(sum(p[[3]]), 100) 120 | expect_equal(dim(p), c(3, 3)) 121 | expect_equal(sum(p[[2]]), nrow(q)) 122 | } 123 | ) 124 | 125 | testthat::test_that("sessions_length function test: checks data table columns type and dimensions ", { 126 | q <- sessions_length(spotifyviz::clean_stream_his, 10) 127 | expect_equal(dim(q), c(6, 2)) 128 | expect_true(class(q[[2]]) == "Duration") 129 | expect_true(class(q[[1]]) == "numeric") 130 | 131 | }) 132 | -------------------------------------------------------------------------------- /tests/testthat/DataFromSpotify/StreamingHistoryTest.json: -------------------------------------------------------------------------------- 1 | [{"endTime":"2018-11-04 18:50","artistName":"Rush","trackName":"Far Cry","msPlayed":318546},{"endTime":"2018-11-04 18:56","artistName":"Rush","trackName":"Test For Echo - Remastered","msPlayed":355666},{"endTime":"2018-11-04 19:04","artistName":"Rush","trackName":"Clockwork Angels","msPlayed":451440},{"endTime":"2018-11-04 19:09","artistName":"Rush","trackName":"The Big Money","msPlayed":334493},{"endTime":"2018-11-04 19:14","artistName":"Rush","trackName":"Bravado - Remastered","msPlayed":275120},{"endTime":"2018-11-04 19:20","artistName":"Rush","trackName":"Animate","msPlayed":363440},{"endTime":"2018-11-04 19:24","artistName":"Rush","trackName":"Ghost Rider","msPlayed":193796},{"endTime":"2018-11-04 19:25","artistName":"Alice In Chains","trackName":"Rooster","msPlayed":6000},{"endTime":"2018-11-04 19:25","artistName":"John Cage","trackName":"In A Landscape","msPlayed":47000},{"endTime":"2018-11-04 19:31","artistName":"Rise Against","trackName":"Savior","msPlayed":242280},{"endTime":"2018-11-04 19:35","artistName":"Rise Against","trackName":"Satellite","msPlayed":238573},{"endTime":"2018-11-04 19:38","artistName":"Rise Against","trackName":"Prayer Of The Refugee","msPlayed":199386},{"endTime":"2018-11-04 19:43","artistName":"Rise Against","trackName":"Hero Of War","msPlayed":253080},{"endTime":"2018-11-04 19:46","artistName":"Rise Against","trackName":"The Violence","msPlayed":228720},{"endTime":"2018-11-04 19:50","artistName":"Rise Against","trackName":"I Don’t Want To Be Here Anymore","msPlayed":239960},{"endTime":"2018-11-04 19:53","artistName":"Rise Against","trackName":"Give It All","msPlayed":170893},{"endTime":"2018-11-04 19:56","artistName":"Rise Against","trackName":"House On Fire","msPlayed":194506},{"endTime":"2018-11-04 20:00","artistName":"Rise Against","trackName":"Make It Stop (September's Children)","msPlayed":234786},{"endTime":"2018-11-04 20:04","artistName":"Rise Against","trackName":"Swing Life Away","msPlayed":200066},{"endTime":"2018-11-04 20:08","artistName":"Rise Against","trackName":"The Violence - Ghost Note Symphonies","msPlayed":260796},{"endTime":"2018-11-04 20:12","artistName":"Rise Against","trackName":"Audience Of One - Ghost Note Symphonies","msPlayed":248697},{"endTime":"2018-11-04 20:15","artistName":"Rise Against","trackName":"Faint Resemblance - Ghost Note Symphonies","msPlayed":142892},{"endTime":"2018-11-04 20:18","artistName":"Rise Against","trackName":"House On Fire - Ghost Note Symphonies","msPlayed":204381},{"endTime":"2018-11-04 20:20","artistName":"Rise Against","trackName":"Like The Angel - Ghost Note Symphonies","msPlayed":137458},{"endTime":"2018-11-04 20:21","artistName":"Goldfinger","trackName":"99 Red Balloons","msPlayed":3870},{"endTime":"2018-11-04 20:21","artistName":"Rise Against","trackName":"Savior - Ghost Note Symphonies","msPlayed":31890},{"endTime":"2018-11-04 20:21","artistName":"Rise Against","trackName":"Miracle - Ghost Note Symphonies","msPlayed":27850},{"endTime":"2018-11-04 20:26","artistName":"Marek Grechuta","trackName":"Dni, których nie znamy","msPlayed":295600},{"endTime":"2018-11-04 20:32","artistName":"Perfect","trackName":"Nie Płacz Ewka","msPlayed":342000},{"endTime":"2018-11-04 21:01","artistName":"Bajm","trackName":"Biała armia","msPlayed":274800},{"endTime":"2018-11-04 21:06","artistName":"Zbigniew Wodecki","trackName":"Zacznij od Bacha","msPlayed":270573},{"endTime":"2018-11-04 21:10","artistName":"Myslovitz","trackName":"Dlugosc Dzwieku Samotnosci","msPlayed":244480},{"endTime":"2018-11-04 21:13","artistName":"Chłopcy Z Placu Broni","trackName":"Kocham Wolność","msPlayed":204360},{"endTime":"2018-11-04 21:17","artistName":"Lady Pank","trackName":"Na Co Komu Dziś","msPlayed":223413},{"endTime":"2018-11-04 21:22","artistName":"Kobranocka","trackName":"Kocham Cię jak Irlandię","msPlayed":294080},{"endTime":"2018-11-04 21:26","artistName":"Maryla Rodowicz","trackName":"Małgośka","msPlayed":268173},{"endTime":"2018-11-04 21:31","artistName":"T.Love","trackName":"Warszawa - 2008 Remastered Version","msPlayed":252920},{"endTime":"2018-11-04 21:42","artistName":"Perfect","trackName":"Autobiografia","msPlayed":147359},{"endTime":"2018-11-04 21:44","artistName":"Queen","trackName":"Party - Remastered 2011","msPlayed":144893},{"endTime":"2018-11-04 21:47","artistName":"Queen","trackName":"Khashoggi's Ship - Remastered 2011","msPlayed":167666},{"endTime":"2018-11-04 21:52","artistName":"Queen","trackName":"The Miracle - Remastered 2011","msPlayed":301360},{"endTime":"2018-11-04 21:57","artistName":"Queen","trackName":"I Want It All - Remastered 2011","msPlayed":280480},{"endTime":"2018-11-04 22:01","artistName":"Queen","trackName":"The Invisible Man - Remastered 2011","msPlayed":237680},{"endTime":"2018-11-04 22:05","artistName":"Queen","trackName":"Breakthru - Remastered 2011","msPlayed":248386},{"endTime":"2018-11-04 22:09","artistName":"Queen","trackName":"Rain Must Fall - Remastered 2011","msPlayed":263293},{"endTime":"2018-11-04 22:14","artistName":"Queen","trackName":"Scandal - Remastered 2011","msPlayed":282920},{"endTime":"2018-11-04 22:17","artistName":"Queen","trackName":"My Baby Does Me - Remastered 2011","msPlayed":202800},{"endTime":"2018-11-04 22:23","artistName":"Queen","trackName":"Was It All Worth It - Remastered 2011","msPlayed":347800},{"endTime":"2018-11-04 22:26","artistName":"Queen","trackName":"Seven Seas Of Rhye - Remastered 2011","msPlayed":168973},{"endTime":"2018-11-04 22:30","artistName":"AC/DC","trackName":"Highway to Hell","msPlayed":208400},{"endTime":"2018-11-04 23:02","artistName":"Black Sabbath","trackName":"Paranoid","msPlayed":166693},{"endTime":"2018-11-04 23:03","artistName":"Queen","trackName":"One Vision - Remastered 2011","msPlayed":1540},{"endTime":"2018-11-04 23:03","artistName":"Steve Miller Band","trackName":"Take The Money And Run","msPlayed":43003},{"endTime":"2018-11-04 23:07","artistName":"Queen","trackName":"Radio Ga Ga - Remastered","msPlayed":210570},{"endTime":"2018-11-04 23:34","artistName":"Genesis","trackName":"Dancing With The Moonlit Knight - Remastered 2008","msPlayed":272470},{"endTime":"2018-11-05 05:20","artistName":"Genesis","trackName":"Dancing With The Moonlit Knight - Remastered 2008","msPlayed":483053},{"endTime":"2018-11-05 05:24","artistName":"Genesis","trackName":"I Know What I Like (In Your Wardrobe) - Remastered 2008","msPlayed":250400},{"endTime":"2018-11-05 05:33","artistName":"Genesis","trackName":"Firth Of Fifth - Remastered 2008","msPlayed":574840},{"endTime":"2018-11-05 05:37","artistName":"Genesis","trackName":"More Fool Me - Remastered 2008","msPlayed":190640},{"endTime":"2018-11-05 05:48","artistName":"Genesis","trackName":"The Battle Of Epping Forest - Remastered 2008","msPlayed":703400},{"endTime":"2018-11-05 05:53","artistName":"Genesis","trackName":"After The Ordeal - Remastered 2008","msPlayed":255040},{"endTime":"2018-11-05 06:09","artistName":"Genesis","trackName":"The Cinema Show - Remastered 2008","msPlayed":641320},{"endTime":"2018-11-05 06:11","artistName":"Genesis","trackName":"More Fool Me - Remastered 2008","msPlayed":0},{"endTime":"2018-11-05 06:11","artistName":"Genesis","trackName":"Aisle Of Plenty - Remastered 2008","msPlayed":118186},{"endTime":"2018-11-05 06:11","artistName":"Genesis","trackName":"That's All - Digital Remastered 2007","msPlayed":86},{"endTime":"2018-11-05 06:11","artistName":"Genesis","trackName":"Dance On A Volcano - 2007 - Remaster","msPlayed":0},{"endTime":"2018-11-05 06:11","artistName":"Genesis","trackName":"Abacab - 2007 Digital Remaster","msPlayed":108},{"endTime":"2018-11-05 06:11","artistName":"Genesis","trackName":"Watcher Of The Skies - Digital Remastered 2008","msPlayed":395},{"endTime":"2018-11-05 06:11","artistName":"Genesis","trackName":"Firth Of Fifth - Remastered 2008","msPlayed":0},{"endTime":"2018-11-05 06:11","artistName":"Genesis","trackName":"Firth Of Fifth - Remastered 2008","msPlayed":271},{"endTime":"2018-11-05 06:11","artistName":"Genesis","trackName":"No Son Of Mine - 2007 Digital Remaster","msPlayed":247},{"endTime":"2018-11-05 06:11","artistName":"Genesis","trackName":"No Son Of Mine - 2007 Digital Remaster","msPlayed":94},{"endTime":"2018-11-05 06:11","artistName":"Genesis","trackName":"Jesus He Knows Me - 2007 Digital Remaster","msPlayed":175},{"endTime":"2018-11-05 06:11","artistName":"Genesis","trackName":"A Trick Of The Tail - Remaster 2007","msPlayed":0},{"endTime":"2018-11-05 06:11","artistName":"Genesis","trackName":"Turn It On Again - Remastered","msPlayed":0},{"endTime":"2018-11-05 06:11","artistName":"Genesis","trackName":"Follow You Follow Me - Remaster 2007","msPlayed":0},{"endTime":"2018-11-05 06:12","artistName":"Genesis","trackName":"No Son Of Mine - 2007 Digital Remaster","msPlayed":23743},{"endTime":"2018-11-05 06:14","artistName":"Styx","trackName":"Mr. Roboto","msPlayed":105690},{"endTime":"2018-11-05 06:18","artistName":"10cc","trackName":"The Wall Street Shuffle","msPlayed":241572},{"endTime":"2018-11-05 06:19","artistName":"Genesis","trackName":"Second Home By The Sea - Digital Remastered 2007","msPlayed":1914},{"endTime":"2018-11-05 06:19","artistName":"Supertramp","trackName":"Crime Of The Century - Remastered","msPlayed":35867},{"endTime":"2018-11-05 06:24","artistName":"Kansas","trackName":"Carry on Wayward Son","msPlayed":323000},{"endTime":"2018-11-05 06:25","artistName":"Electric Light Orchestra","trackName":"Evil Woman","msPlayed":1777},{"endTime":"2018-11-05 06:25","artistName":"Roger Waters","trackName":"Picture That","msPlayed":81630},{"endTime":"2018-11-05 06:25","artistName":"Genesis","trackName":"Turn It On Again - Remastered","msPlayed":1588},{"endTime":"2018-11-05 06:26","artistName":"Emerson, Lake & Palmer","trackName":"Karn Evil 9 1st Impression, Pt. 2 - 2014 Remastered Version","msPlayed":2792},{"endTime":"2018-11-05 06:26","artistName":"Frank Zappa","trackName":"Uncle Remus","msPlayed":31317},{"endTime":"2018-11-05 06:30","artistName":"My Bloody Valentine","trackName":"Only Shallow - Remastered Version","msPlayed":255923},{"endTime":"2018-11-05 06:30","artistName":"Metallica","trackName":"Hit The Lights - Remastered","msPlayed":2328},{"endTime":"2018-11-05 06:31","artistName":"Metallica","trackName":"Seek & Destroy - Remastered","msPlayed":52237},{"endTime":"2018-11-05 06:39","artistName":"New Order","trackName":"Blue Monday - 2016 Remaster","msPlayed":450800},{"endTime":"2018-11-05 06:41","artistName":"New Order","trackName":"Bizarre Love Triangle","msPlayed":133751},{"endTime":"2018-11-05 06:41","artistName":"Rush","trackName":"A Farewell To Kings","msPlayed":10951},{"endTime":"2018-11-05 06:41","artistName":"Deep Purple","trackName":"Burn - Remastered 2004","msPlayed":3482},{"endTime":"2018-11-05 06:51","artistName":"Metallica","trackName":"One","msPlayed":479472},{"endTime":"2018-11-05 14:11","artistName":"Metallica","trackName":"One","msPlayed":358685},{"endTime":"2018-11-05 14:16","artistName":"Black Sabbath","trackName":"Sabbath Bloody Sabbath","msPlayed":347626},{"endTime":"2018-11-05 14:23","artistName":"Black Sabbath","trackName":"A National Acrobat","msPlayed":375106},{"endTime":"2018-11-05 14:29","artistName":"Black Sabbath","trackName":"Fluff","msPlayed":213879},{"endTime":"2018-11-05 14:30","artistName":"Iron Maiden","trackName":"Afraid To Shoot Strangers - 1998 Remastered Version","msPlayed":106554}] 2 | -------------------------------------------------------------------------------- /vignettes/basic.Rmd: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | title: "Basic of SpotifyViz" 4 | author: " " 5 | date: "`r Sys.Date()`" 6 | output: 7 | rmarkdown::html_vignette: 8 | toc: true 9 | toc_depth : 2 10 | vignette: > 11 | %\VignetteIndexEntry{Basic of SpotifyViz} 12 | %\VignetteEngine{knitr::rmarkdown} 13 | %\VignetteEncoding{UTF-8} 14 | --- 15 | 16 | ```{r setup, include = FALSE} 17 | knitr::opts_chunk$set( 18 | collapse = TRUE, 19 | comment = "#>", 20 | message = FALSE , 21 | warning = FALSE, 22 | fig.width = 7, 23 | fig.height = 5 24 | 25 | ) 26 | ``` 27 | 28 | ```{r, warning=F} 29 | library(spotifyviz) 30 | ``` 31 | 32 | This vignette introduces to basic functionalities of spotifyviz package. 33 | 34 | # Loading data 35 | 36 | 37 | To analyze data from spotify first we have to load the data. We're going to use `complete_streaming_history` to load streaming history, `prepare_playlists` to load playlists and `make_search_queries_dt` for search queries. These functions take as an argument the path to the folder where the data from spotify is kept. They assume that all data in this folder is from one spotify user, so if you have data from more than one user keep them in separate folders. 38 | 39 | ```{r, eval = FALSE} 40 | 41 | streaming_history <- complete_streaming_history("path") 42 | 43 | search_queries <- make_search_queries_dt("path") 44 | 45 | playlists <- prepare_playlists("path") 46 | 47 | ``` 48 | 49 | 50 | In further examples data provided by the package will be used. 51 | 52 | ```{r} 53 | streaming_history <- spotifyviz::clean_stream_his 54 | 55 | search_queries <- spotifyviz::clean_search_queries 56 | 57 | playlists <- spotifyviz::clean_playlist 58 | 59 | 60 | ``` 61 | 62 | 63 | The loaded data looks like this: 64 | 65 | ```{r} 66 | 67 | head(streaming_history) 68 | 69 | head(search_queries) 70 | 71 | head(playlists) 72 | ``` 73 | 74 | # Preparing the additional data 75 | 76 | ## str_his_with_playlists_wide, str_his_with_playlist_long 77 | This functions make the data table containing information about user streaming history with additional information on whether the track was part of one of the playlist provided by the playlist data table. The wide version gives the output in the wide form, and the long version in the long form. 78 | 79 | 80 | ### Example 81 | 82 | ```{r} 83 | # the wide form 84 | # playlist_dt = playlists : a data table containing user 85 | # playlists, made with prepare_playlis 86 | # streaming_history : = streaming_history : part of streaming 87 | # history of one user, 88 | # made with complete_streaming_history 89 | 90 | playlists_wide <- str_his_with_playlists_wide(playlists, streaming_history) 91 | 92 | # see the data 93 | 94 | head(playlists_wide) 95 | 96 | # the long form 97 | 98 | # playlist_dt = playlists : a data table containing user 99 | # playlists, made with prepare_playlis 100 | # streaming_history : = streaming_history : part of streaming 101 | # history of one user, made with complete_streaming_history 102 | 103 | 104 | playlist_long <- str_his_with_playlists_long(playlists, streaming_history) 105 | 106 | # see the data 107 | 108 | head(playlist_long) 109 | ``` 110 | 111 | 112 | ## continuous_listening 113 | 114 | This function adds the "listening number" to the streaming history data table - the number which determines to which listening session (the session - the tracks were played with less than `mins` minutes of breaks between them) the track belongs. 115 | 116 | ###Example 117 | 118 | ```{r} 119 | # streaming_history = streaming_history : streaming_history : 120 | # part of streaming history of one user, made with 121 | # complete_streaming_history 122 | # mins = 5 : the breaks must be less than 5 minutes 123 | sessions <- continuous_listening(streaming_history, mins = 5) 124 | 125 | # see the data 126 | head(sessions) 127 | 128 | ``` 129 | 130 | 131 | # Stats and summaries of the data 132 | 133 | ## make_summary_dt 134 | 135 | This function makes the table with brief summary of the data filtered to the period of time provided. It uses `measure_how_long_listened` and `count_skipped` functions. 136 | 137 | # Example 138 | ```{r} 139 | # streaming_history = streaming_history : part of streaming 140 | # history of one user, made with complete_streaming_history 141 | # start_date = "2018-01-01" : indicates start of the period 142 | # of time should be on "2018-01-01" 143 | # end_date = "2019-01-01" : indicates end of the period 144 | # of time should be on "2019-01-01" 145 | # as_percentage = TRUE : tracks skipped and time listened 146 | # to will be a percentegae of all tracks and whole time period. 147 | make_summary_dt(streaming_history, start_date = "2018-01-01", end_date = "2019-01-01", as_percentage = TRUE) 148 | ``` 149 | 150 | ## most_played 151 | 152 | This function finds the most played tracks or artist and returns a data table with either track or artist name and the times it was played. 153 | 154 | ### Example 155 | 156 | ```{r} 157 | # streaming_history = streaming_history : part of streaming 158 | # history of one user, made with complete_streaming_history 159 | # track_or_artist = "artist" : count how many times each artist 160 | # was played 161 | # show_skipped = FALSE : don't include skipped tracks in the count 162 | # how_many = 10 : show first ten results 163 | 164 | most_played(streaming_history, track_or_artist = "artist", show_skipped = FALSE, how_many = 10) 165 | ``` 166 | 167 | 168 | ## most_skipped 169 | 170 | This function finds the most skipped (their play time was less than 10s) tracks or artist and returns a data table with either artist or track names and the times they were skipped. 171 | 172 | ### Example 173 | 174 | ```{r} 175 | # streaming_history = streaming_history : part of streaming 176 | # history of one user, made with complete_streaming_history 177 | # track_or_artist = "artist" : count how many times each 178 | # artist was played 179 | # for_view = TRUE : show results in format good for reading 180 | # but not the best for futher computations 181 | # how_many = 10 : show first ten results 182 | 183 | most_skipped(streaming_history, track_or_artist = "track", for_view = T, how_many = 10) 184 | ``` 185 | 186 | ## measure_how_long_listened 187 | 188 | This function calculates how long the user listened to spotify in given time period. It can give the result as the duration or as the percentage of the specified time period. 189 | 190 | ### Example 191 | 192 | ```{r} 193 | # streaming_history = streaming_history : part of streaming 194 | # history of one user, made with complete_streaming_history 195 | # start_date = "2018-01-01" : filter the data table to only 196 | # include results later than "2018-01-01" 197 | # end_date = "2019-01-01" : filter the data table to only 198 | # include results earlier than "2019-01-01" 199 | # as_percentage = TRUE : show the result as a percentage of the 200 | # input time period from strat_date to end_date 201 | # for_view = TRUE : show results in format good for reading but 202 | # not the best for further computations 203 | # 204 | measure_how_long_listened(streaming_history, start_date = "2018-01-01", end_date = "2019-01-01", as_percentage = TRUE, for_viewing = TRUE) 205 | ``` 206 | 207 | 208 | ## count_skipped 209 | 210 | This function counts how many tracks were skipped (their play time was less than 10s) in given time period. Either as a number or as a percentage of all tracks played in given time period. 211 | 212 | ### Example 213 | 214 | ```{r} 215 | # streaming_history = streaming_history : part of streaming 216 | # history of one user, made with complete_streaming_history 217 | # start_date = "2018-01-01" : filter the data table to only 218 | # include results later than "2018-01-01" 219 | # end_date = "2019-01-01" : filter the data table to only 220 | # include results earlier than "2019-01-01" 221 | # as_percentage = TRUE : show the result as a percentage 222 | # of all tracks played from strat_date to end_date 223 | 224 | 225 | count_skipped(streaming_history, start_date = "2018-01-01", end_date = "2019-01-01", as_percentage = TRUE) 226 | ``` 227 | 228 | ## longest_session 229 | 230 | This function finds the longest listening session (see `continuous_listening`) and makes the data table with it. 231 | 232 | ### Example 233 | 234 | ```{r} 235 | # streaming_history = streaming_history : streaming_history : 236 | # part of streaming history of one user, 237 | # made with complete_streaming_history 238 | # mins = 5 : the breaks must be less than 5 minutes 239 | long_session <- longest_session(streaming_history, mins = 5) 240 | 241 | #see the data 242 | head(long_session) 243 | ``` 244 | 245 | ## make_session_stats 246 | 247 | This function calculates and returns the stats of the single listening session. 248 | 249 | ### Example 250 | 251 | ```{r} 252 | # session_dt = long_session : see the stats of the longest 253 | # session found earlier with longest_session function 254 | make_session_stats(long_session) 255 | ``` 256 | 257 | ## session_length 258 | 259 | This function shows about length of every session. Excludes skipped tracks. 260 | 261 | ### Example 262 | 263 | ```{r} 264 | # streaming_history = streaming_history : streaming_history : 265 | # part of streaming history of one user, 266 | # made with complete_streaming_history 267 | # mins = 5 : the breaks must be less than 5 minutes 268 | # see the data 269 | head(sessions_length(streaming_history, mins = 5)) 270 | ``` 271 | 272 | ## session_intervals 273 | 274 | This function makes and returns the table with sessions divided into listening intervals. 275 | Sessions length are divided into categories: 276 | 277 | * <10min 278 | * 10-30min 279 | * 0.5-1h 280 | * 1-2h 281 | * 2-4h 282 | * 4-6h 283 | * 6h+ 284 | 285 | ```{r} 286 | # streaming_history = streaming_history : streaming_history : 287 | # part of streaming history of one user, 288 | # made with complete_streaming_history 289 | # mins = 5 : the breaks must be less than 5 minutes 290 | 291 | # see the table 292 | 293 | head(sessions_intervals(streaming_history, mins = 5)) 294 | ``` 295 | 296 | # Plots 297 | 298 | ## plot_searches 299 | 300 | This function plots number of search queries performed by user on different days. As additional information it can show either from which platform or in which country did the user perform the search. 301 | 302 | ### Example 303 | 304 | ```{r} 305 | # search_queries = search_queries : part of search queries 306 | # of one user, made with search_qeuries_dt 307 | # additional = "platform" : show which platform the user used 308 | plot_searches(search_queries, additional = "platform") 309 | ``` 310 | 311 | 312 | ## plot_in_playlists_count 313 | 314 | This function plots the number of tracks or the time played for tracks from users playlist and other tracks. Can be shown as a percentage. 315 | 316 | ## Example 317 | ```{r} 318 | # str_his_with_playlist_wide = playlists_wide : use the wide 319 | # format of the data table with streaming history and playlists 320 | # as_percentage = FALSE : show as a value and not as a percentage 321 | # time_or_count = "time" : count the sum of playtimes 322 | # time_unit = "hours" : set the unit of time shown to hours 323 | plot_in_playlists_count(playlists_wide, as_percentage = FALSE, time_or_count = "time", time_unit = "hours") 324 | ``` 325 | 326 | 327 | ## plot_playlist_popularity 328 | 329 | This function plots the popularity of the playlists - either counting the number of time tracks from it were played or the time they were played. 330 | 331 | ### Example 332 | 333 | ```{r} 334 | # str_his_with_playlist_long = playlists_long : use the long 335 | # format of the data table with streaming history and playlists 336 | # time_or_count = "time" : count the sum of playtimes 337 | # time_unit = "hours" : set the unit of time shown to hours 338 | 339 | plot_playlist_popularity(playlist_long, time_or_count = "time", time_unit = "hours") 340 | ``` 341 | 342 | ## plot_track_count 343 | 344 | This function plots how many tracks were played overtime, split by some period of time. 345 | 346 | # Example 347 | 348 | ```{r} 349 | # streaming_history = streaming_history : part of streaming 350 | # history of one user, made with complete_streaming_history 351 | # only_skipped = FALSE : show skipped and played tracks 352 | # by = "day" : split the dateline by day 353 | plot_track_count(streaming_history, only_skipped = FALSE, by = "day") 354 | ``` 355 | 356 | ## plot_track_count_by_period 357 | 358 | This function plots either daily or hourly habits of listening of the user. 359 | 360 | # Example 361 | 362 | ```{r} 363 | # streaming_history = streaming_history : part of streaming 364 | # history of one user, made with complete_streaming_history 365 | # include_skipped = FALSE : don't show skipped tracks 366 | # period = "hour" : show hourly habbits 367 | # as_percentage = FALSE : don't as the percentage 368 | # by_weekday = TRUE : split the plot by weekdays 369 | plot_track_count_by_period(streaming_history, period = "hour", as_percentage = FALSE, include_skipped = FALSE, by_weekday = TRUE) 370 | ``` 371 | 372 | ##sessions_visualize 373 | This function plots the number of sessions for different session lengths.Sessions length are divided into categories: 374 | 375 | * <10min 376 | * 10-30min 377 | * 0.5-1h 378 | * 1-2h 379 | * 2-4h 380 | * 4-6h 381 | * 6h+ 382 | 383 | ```{r} 384 | # streaming_history = streaming_history : part of streaming 385 | # history of one user, made with complete_streaming_history 386 | # mins = 5 : the breaks must be less than 5 minutes 387 | # as_percentage = TRUE : show as a percentage of the number 388 | # of all sessions 389 | sessions_visualize(streaming_history, mins = 5, as_percentage = TRUE) 390 | 391 | ``` 392 | 393 | -------------------------------------------------------------------------------- /tests/testthat/DataFromSpotify/PlaylistTest.json: -------------------------------------------------------------------------------- 1 | {"playlists":[{"name":"Urodzinowa playlista","lastModifiedDate":"2018-12-08","items":[{"track":{"trackName":"Gimme! Gimme! Gimme! (A Man After Midnight)","artistName":"ABBA","albumName":"Voulez-Vous"}},{"track":{"trackName":"Girls Just Want to Have Fun","artistName":"Cyndi Lauper","albumName":"She's So Unusual"}},{"track":{"trackName":"Biała armia","artistName":"Bajm","albumName":"Biala Armia"}},{"track":{"trackName":"U Can't Touch This","artistName":"MC Hammer","albumName":"Please Hammer Don't Hurt 'Em"}},{"track":{"trackName":"Don't Stop Me Now - Remastered","artistName":"Queen","albumName":"Jazz"}},{"track":{"trackName":"I Just Called To Say I Love You","artistName":"Stevie Wonder","albumName":"At The Close Of A Century"}},{"track":{"trackName":"My Heart Will Go On - Love Theme from \"Titanic\"","artistName":"Céline Dion","albumName":"My Heart Will Go On"}},{"track":{"trackName":"Africa","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Voulez-Vous","artistName":"ABBA","albumName":"Voulez-Vous"}},{"track":{"trackName":"You Spin Me Round (Like a Record)","artistName":"Dead Or Alive","albumName":"Youthquake"}},{"track":{"trackName":"Girl Goodbye","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"I'll Supply the Love","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Georgy Porgy","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Hold the Line","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Manuela Run","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Rockmaker","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"You Are the Flower","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"St. George and the Dragon","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"99","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"All Us Boys","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Hydra","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Lorraine","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Mama","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"A Secret Love","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Turn Back","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"If It's the Last Night","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Live for Today","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Gift with a Golden Gun","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Million Miles Away","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Rosanna","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Africa","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"I Won't Hold You Back","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Make Believe","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"It's a Feeling","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Waiting for Your Love","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Endless","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"How Does It Feel","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Holyanna","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Stranger In Town","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Can't Stand It Any Longer","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Without Your Love","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Anna","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Straight for the Heart","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Pamela","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Stop Loving You","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Don't Chain My Heart","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"2 Hearts","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Only You","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Kingdom of Desire","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"The Turning Point","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"If You Belong to Me","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"I Will Remember","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Caught In the Balance","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Cruel","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Melanie - Radio Edit","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Girl Goodbye","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"I'll Supply the Love","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Georgy Porgy","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Hold the Line","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Manuela Run","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Rockmaker","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"You Are the Flower","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"St. George and the Dragon","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"99","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"All Us Boys","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Hydra","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Lorraine","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Mama","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"A Secret Love","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Turn Back","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"If It's the Last Night","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Live for Today","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Gift with a Golden Gun","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Million Miles Away","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Rosanna","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"I Won't Hold You Back","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Make Believe","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"It's a Feeling","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Waiting for Your Love","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Endless","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"How Does It Feel","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Holyanna","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Stranger In Town","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Can't Stand It Any Longer","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Without Your Love","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Anna","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Straight for the Heart","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Pamela","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Stop Loving You","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Don't Chain My Heart","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"2 Hearts","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Only You","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Kingdom of Desire","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"The Turning Point","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"If You Belong to Me","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"I Will Remember","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Caught In the Balance","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Cruel","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Melanie - Radio Edit","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Money Changes Everything","artistName":"Cyndi Lauper","albumName":"She's So Unusual"}},{"track":{"trackName":"When You Were Mine","artistName":"Cyndi Lauper","albumName":"She's So Unusual"}},{"track":{"trackName":"Time After Time","artistName":"Cyndi Lauper","albumName":"She's So Unusual"}},{"track":{"trackName":"She Bop","artistName":"Cyndi Lauper","albumName":"She's So Unusual"}},{"track":{"trackName":"All Through the Night","artistName":"Cyndi Lauper","albumName":"She's So Unusual"}},{"track":{"trackName":"Witness","artistName":"Cyndi Lauper","albumName":"She's So Unusual"}},{"track":{"trackName":"I'll Kiss You","artistName":"Cyndi Lauper","albumName":"She's So Unusual"}},{"track":{"trackName":"He's so Unusual","artistName":"Cyndi Lauper","albumName":"She's So Unusual"}},{"track":{"trackName":"Yeah Yeah","artistName":"Cyndi Lauper","albumName":"She's So Unusual"}},{"track":{"trackName":"Crazy Little Thing Called Love - Remastered 2011","artistName":"Queen","albumName":"The Game"}},{"track":{"trackName":"Sunny","artistName":"Boney M.","albumName":"Take The Heat Off Me"}},{"track":{"trackName":"Boogie Wonderland","artistName":"Earth, Wind & Fire","albumName":"I Am"}},{"track":{"trackName":"You're The One That I Want","artistName":"Various Artists","albumName":"Grease"}},{"track":{"trackName":"Girls Just Want to Have Fun","artistName":"Cyndi Lauper","albumName":"She's So Unusual"}},{"track":{"trackName":"Maniac","artistName":"Various Artists","albumName":"Flashdance Original Soundtrack From The Motion Picture"}},{"track":{"trackName":"Night Fever","artistName":"Bee Gees","albumName":"Greatest"}},{"track":{"trackName":"Tell It to My Heart","artistName":"Taylor Dayne","albumName":"Greatest Hits"}},{"track":{"trackName":"Mambo No. 5 (A Little Bit of...)","artistName":"Lou Bega","albumName":"King of Mambo"}},{"track":{"trackName":"Super Trouper","artistName":"ABBA","albumName":"ABBA Gold"}},{"track":{"trackName":"Lay All Your Love On Me","artistName":"ABBA","albumName":"Super Trouper"}},{"track":{"trackName":"Money, Money, Money","artistName":"ABBA","albumName":"ABBA Gold"}},{"track":{"trackName":"Mój Przyjacielu (Moi drugovi)","artistName":"Various Artists","albumName":"Marek Sierocki Przedstawia: I love Polska 3"}},{"track":{"trackName":"The Final Countdown","artistName":"Various Artists","albumName":"On Your 80's Radio"}},{"track":{"trackName":"Wake Me Up Before You Go-Go","artistName":"Wham!","albumName":"Make It Big"}},{"track":{"trackName":"Myśli i słowa","artistName":"Bajm","albumName":"Mysli I Slowa"}},{"track":{"trackName":"Mniej niż zero","artistName":"Lady Pank","albumName":"Lady Pank"}},{"track":{"trackName":"Tacy sami","artistName":"Lady Pank","albumName":"Tacy sami"}},{"track":{"trackName":"Africa","artistName":"TOTO","albumName":"Hold The Line: The Ultimate Toto Collection"}},{"track":{"trackName":"Walking On Sunshine","artistName":"Katrina & The Waves","albumName":"Katrina & The Waves"}},{"track":{"trackName":"Dancing with Tears in My Eyes","artistName":"Ultravox","albumName":"The Collection"}},{"track":{"trackName":"Start Me Up - 2009 Re-Mastered Digital Version","artistName":"The Rolling Stones","albumName":"Tattoo You"}},{"track":{"trackName":"(I Can't Get No) Satisfaction - Mono Version / Remastered 2002","artistName":"The Rolling Stones","albumName":"Out Of Our Heads"}},{"track":{"trackName":"Holding Out for a Hero - From \"Footloose\" Soundtrack","artistName":"Various Artists","albumName":"Footloose (15th Anniversary Collectors' Edition)"}},{"track":{"trackName":"Jumpin' Jack Flash - Mono Version","artistName":"The Rolling Stones","albumName":"Through The Past, Darkly (Big Hits Vol. 2)"}},{"track":{"trackName":"You Won't See Me - Remastered","artistName":"The Beatles","albumName":"Rubber Soul"}},{"track":{"trackName":"Ticket To Ride - Remastered","artistName":"The Beatles","albumName":"Help!"}},{"track":{"trackName":"Nic nie może wiecznie trwać","artistName":"Anna Jantar","albumName":"Anna Jantar"}},{"track":{"trackName":"Kocham wolność","artistName":"Chłopcy Z Placu Broni","albumName":"Gwiazdy spadają z nieba"}},{"track":{"trackName":"Jedwab","artistName":"Różni Wykonawcy","albumName":"Polskie ballady rockowe"}},{"track":{"trackName":"Don't Bring Me Down","artistName":"Electric Light Orchestra","albumName":"The Essential Electric Light Orchestra"}},{"track":{"trackName":"A Hard Day's Night - Remastered","artistName":"The Beatles","albumName":"A Hard Day's Night"}},{"track":{"trackName":"Twist And Shout - Remastered","artistName":"The Beatles","albumName":"Please Please Me"}},{"track":{"trackName":"She's Like the Wind (feat. Wendy Fraser)","artistName":"Various Artists","albumName":"Dirty Dancing (Original Motion Picture Soundtrack)"}},{"track":{"trackName":"Summer Nights - From “Grease” Soundtrack","artistName":"Various Artists","albumName":"Grease"}},{"track":{"trackName":"It's Only Rock'n'Roll (But I Like It) - 2009 Re-Mastered Digital Version","artistName":"The Rolling Stones","albumName":"It's Only Rock 'N' Roll"}},{"track":{"trackName":"Luxury - Remastered","artistName":"The Rolling Stones","albumName":"It's Only Rock 'N' Roll"}},{"track":{"trackName":"Ain't Too Proud To Beg - Remastered","artistName":"The Rolling Stones","albumName":"It's Only Rock 'N' Roll"}},{"track":{"trackName":"(I've Had) The Time of My Life","artistName":"Various Artists","albumName":"Dirty Dancing"}},{"track":{"trackName":"Superstition - Single Version","artistName":"Stevie Wonder","albumName":"Number Ones"}},{"track":{"trackName":"Take A Chance On Me","artistName":"ABBA","albumName":"The Album"}},{"track":{"trackName":"Chiquitita","artistName":"ABBA","albumName":"Voulez-Vous"}},{"track":{"trackName":"Sexy Sexy Lover - Vocal Version","artistName":"Modern Talking","albumName":"Alone"}},{"track":{"trackName":"Cykady Na Cykladach - 2011 Remastered Version","artistName":"Maanam","albumName":"The Singles Collection [2011 Remaster]"}},{"track":{"trackName":"Szare Miraze","artistName":"Maanam","albumName":"The Best Of Kora & Maanam Volume II"}},{"track":{"trackName":"Boskie Buenos (Buenos Aires)","artistName":"Maanam","albumName":"The Best Of Kora & Maanam Volume 1"}},{"track":{"trackName":"A Little Less Conversation","artistName":"Elvis Presley","albumName":"The Essential Elvis Presley"}},{"track":{"trackName":"Disco Inferno - Single Edit","artistName":"The Trammps","albumName":"Rhino Hi-Five: The Trammps"}},{"track":{"trackName":"You Should Be Dancing","artistName":"Bee Gees","albumName":"The Ultimate Bee Gees"}},{"track":{"trackName":"I Was Made For Lovin' You","artistName":"KISS","albumName":"Dynasty"}},{"track":{"trackName":"When a Man Loves a Woman","artistName":"Michael Bolton","albumName":"Time, Love & Tenderness"}},{"track":{"trackName":"Maneater","artistName":"Daryl Hall & John Oates","albumName":"H2O"}},{"track":{"trackName":"I Want to Know What Love Is - 1999 Remaster","artistName":"Foreigner","albumName":"Agent Provocateur"}},{"track":{"trackName":"(I Just) Died In Your Arms","artistName":"Cutting Crew","albumName":"The Best Of Cutting Crew"}},{"track":{"trackName":"With Or Without You - Remastered","artistName":"U2","albumName":"The Joshua Tree"}},{"track":{"trackName":"Nothing's Gonna Stop Us Now","artistName":"Starship","albumName":"No Protection"}},{"track":{"trackName":"Let's Twist Again (Remastered)","artistName":"Various Artists","albumName":"Las 66 Favoritas de Jose María Ínigo y José Ramón Pardo. Vol. 1 (1958-1961) [Remastered]]"}},{"track":{"trackName":"September","artistName":"Earth, Wind & Fire","albumName":"The Eternal Dance"}},{"track":{"trackName":"Somebody's Watching Me","artistName":"Various Artists","albumName":"Motown 40 Forever"}},{"track":{"trackName":"Will You Still Love Me Tomorrow","artistName":"Jay & The Americans","albumName":"Keepin' The Music Alive"}},{"track":{"trackName":"Oh Pretty Woman","artistName":"Jay & The Americans","albumName":"Keepin' The Music Alive"}},{"track":{"trackName":"Let It Be Me","artistName":"Jay & The Americans","albumName":"Keepin' The Music Alive"}},{"track":{"trackName":"Walking In The Moonlight (dancing In the Rain)","artistName":"Jay & The Americans","albumName":"Keepin' The Music Alive"}},{"track":{"trackName":"Sunday And Me","artistName":"Jay & The Americans","albumName":"Keepin' The Music Alive"}},{"track":{"trackName":"Desperate People","artistName":"Jay & The Americans","albumName":"Keepin' The Music Alive"}},{"track":{"trackName":"Love Hurts","artistName":"Jay & The Americans","albumName":"Keepin' The Music Alive"}},{"track":{"trackName":"Only A Heart","artistName":"Jay & The Americans","albumName":"Keepin' The Music Alive"}},{"track":{"trackName":"Crying","artistName":"Jay & The Americans","albumName":"Keepin' The Music Alive"}},{"track":{"trackName":"Welcome To Lonely","artistName":"Jay & The Americans","albumName":"Keepin' The Music Alive"}},{"track":{"trackName":"Keepin' The Music Alive","artistName":"Jay & The Americans","albumName":"Keepin' The Music Alive"}},{"track":{"trackName":"Oh Pretty Woman","artistName":"Jay & The Americans","albumName":"Keepin' The Music Alive"}},{"track":{"trackName":"Jailhouse Rock","artistName":"Elvis Presley","albumName":"Elvis' Golden Records"}},{"track":{"trackName":"Does Your Mother Know","artistName":"ABBA","albumName":"Abba Gold Anniversary Edition"}},{"track":{"trackName":"Isn't She Lovely","artistName":"Stevie Wonder","albumName":"Songs In The Key Of Life"}},{"track":{"trackName":"I Can't Live With You - Remastered 2011","artistName":"Queen","albumName":"Innuendo"}},{"track":{"trackName":"Headlong - Remastered 2011","artistName":"Queen","albumName":"Innuendo"}},{"track":{"trackName":"Heaven Is a Place on Earth","artistName":"Belinda Carlisle","albumName":"Greatest Vol.1 - Belinda Carlisle"}},{"track":{"trackName":"I Still Haven't Found What I'm Looking For - Remastered 2007","artistName":"U2","albumName":"The Joshua Tree"}},{"track":{"trackName":"Joe le taxi","artistName":"Vanessa Paradis","albumName":"M & J"}},{"track":{"trackName":"You Keep Me Hangin On","artistName":"Kim Wilde","albumName":"Another Step"}},{"track":{"trackName":"Livin' On A Prayer","artistName":"Bon Jovi","albumName":"Slippery When Wet"}},{"track":{"trackName":"Total Eclipse of the Heart","artistName":"Bonnie Tyler","albumName":"Faster Than The Speed Of Night"}},{"track":{"trackName":"Moonlight Shadow","artistName":"Mike Oldfield","albumName":"The Mike Oldfield Collection"}},{"track":{"trackName":"You Can't Hurry Love - 2016 Remaster","artistName":"Phil Collins","albumName":"Hello, I Must Be Going!"}},{"track":{"trackName":"YMCA - Original Version 1978","artistName":"Village People","albumName":"YMCA"}},{"track":{"trackName":"Sultans Of Swing","artistName":"Dire Straits","albumName":"Sultans Of Swing - The Very Best Of Dire Straits"}},{"track":{"trackName":"Brown Girl in the Ring","artistName":"Boney M.","albumName":"The Essential Boney M."}},{"track":{"trackName":"Thriller","artistName":"Michael Jackson","albumName":"Michael Jackson's This Is It"}},{"track":{"trackName":"Eye of the Tiger","artistName":"Various Artists","albumName":"Rocky IV"}},{"track":{"trackName":"Blue Monday - 2011 Total Version","artistName":"New Order","albumName":"TOTAL"}},{"track":{"trackName":"Kryzysowa Narzeczona","artistName":"Lady Pank","albumName":"Lady Pank"}}],"description":"Oficjalna playlista na urodziny","numberOfFollowers":9}]} 2 | --------------------------------------------------------------------------------