Background
This is a theoretical problem similar to an actual problem I am having.
Imagine I have a database of millions of grades for students with rows looking something like the following:
student_id | first_name | last_name | maths_grade | english_grade | physics_grade | chemistry_grade | biology_grade
-------------------------------------------------------------------------------------------------------------------
15643 | John | Smith | 68 | 87 | 54 | 36 | 98
13465 | Alice | Jones | 87 | 54 | 52 | 84 | 23
....
The Problem
I want to find students with the most similar results so I can pair them up as study partners, that way they can both learn from each other, unlike a typical mentor/mentee relationship where only one student learns.
When a given student submits a request for a study partner, I want to find the student in our database that has the most similar grades to the student who made a request.
All subject grades have equal weighting.
The Required Solution
Since this database has millions of grades, simply looking through the entire database and computing a 'similarity score' for the given student won't work, it would be too slow and wouldn't scale.
Instead, we need to add a grade_fingerprint field to the database that contains a 'similarity fingerprint' of the students' grades. We can then index the table on this field and quickly find the two closest students to our given student.
This grade_fingerpriny would be similar to a hash code in that it acts as a summary of the table entry, however, two objects with similar properties do not have a similar hash code, which makes them unsuitable.
Accepted Answers
The name of a sub-domain of computer science that covers the fingerprinting of objects in a way that allows for easy searching for smiliar objects
The pseudocode of an algorithm that can compute a 'fingerprint' of an object that can be used to compare object similarity
Related
I'm trying to make a Shiny app that uses some Geographical data I have stored in MySQL. The data currently contains a list of Longitudes and Latitudes (of client location), client age, client gender, and other various demographics. For example:
+--------+-------+--------+---------+
| Member | Long | Lat | Gender |
+--------+-------+--------+---------+
| A | 34 | -118 | M |
| B | 34 | -118 | F |
| C | 41 | -74 | M |
| D | 39 | -77 | M |
+--------+-------+--------+---------+
I want to use leaflet to create a bubble map for the locations - a bigger bubble at a location means that more clients are present at that area. It seems like the function addCircles does this pretty well, but the problem is that I don't have a count of the number of clients at each location to assign to the radius parameter- each row in my table represents information for a particular client, not a location. Is there a way to obtain that info?
My best guess is to create a new table where each row represents counts for a different longitude and latitude and then use a count column to count the number of times that location appears among clients, but I'm not sure if this is the best way since it involves creating a new table, and I would have to create additional columns like "number of males" and "number of females" for every factor I want to account for. And what if I wanted to adjust my map for females who are in the age range of 40-50? The number of columns I would have to create would easily exceed 100..
I want to import data and tidy it in r. I have achieved some of the results I want using functions in Excel, but it is tedious and must be redone by hand each time I get a new Excel file with updated data. I have an Excel file with separate worksheets for each time period. This Excel file is updated multiple times each year, keeping the same style but adding additional data, including adding additional time period worksheets. Each worksheet follows the same format, as follows:
Student_ID| Major_ID | Gender | Age | Semester_Registered | Marital_Status | Home_State
20130001 | 10022 | M | 22 | 3 | S | AZ
20130002 | 10022 | F | 23 | 5 | M | CA
20140001 | 10022 | M | 21 | 3 | M | CA
20140004 | 10034 | F | 24 | 4 | S | AZ
This would be the example for the first few records of a given time period worksheet, let's say 2016_Semester_1. Student ID is assigned to a student when they register for classes and serves as a unique identifier. Major_ID corresponds to a table with Major_ID and Major_Name and Campus. The codes stay the same for each worksheet, but a student can change majors or change campus, thus Major_ID could be different for a given student from one time period to another. Gender and age are self-explanatory. Semester_Registered is a number from 1 to 8. When a student first registers for classes, they are in Semester_Registered 1, then their second semester in their first year they should move on to 2, their first semester of their sophomore year they should be in 3, all the way to 8 in the second semester of their senior year. However, some students do not move through the sequence of semesters at the normal rate, for example if they have to repeat a semester due to failed courses or if they have to leave the university for a time in order to earn more money before returning later and continuing their studies. Marital_Status is either S for Single, M for Married, D for Divorced or W for Widowed. Home_State is the two letter abbreviation for the US State the student is from, mainly needed to see if the student qualifies for in-state tuition rates, but also useful for reports to see where most students come from to focus marketing activities on those states.
The Excel workbook that I have contains a worksheet for each academic semester from 2014_1 to 2019_1. I want to consolidate the data and tidy it in two main ways. First, I want to make new tables for each Freshman class, including only those who were in Semester_Registered 1 in the 2014_1 semester in one table, in the 2015_1 semester in another table, up through 2019_1. The headers for the data I want in these tables would like like this:
First_Semester | Student_ID | Major_ID_Start | Gender | Age_Start | Marital_Status_Start | Final_Semester_Time | Final_Semester_Registered | Graduated_On_Time | Graduated_Late | Major_ID_End | Age_End | Marital_Status_End | Still_Enrolled
All of the records in a given table would have the same First_Semester value, such as 2014_1 or 2015_1. Student_ID is the identifier. Maojor_ID_Start is the Major_ID the student had in First_Semester. Gender could probably be collected onlly once from First_Semester. Age_Start and Marital_Status_Start are their respective values as listed in First_Semester. Final_Semester_Registered needs to look through each time period worksheet until it finds that the given Student_ID no longer appears on the list of registered students; for students who graduate, this should be the time period when Semester_Registered equals 8, but some students drop out before graduation so this would show in which time period they were last registered before dropping out. Final_Semester_Registered is shows the value of Semester_Registered in Final_Semester_Time, which should be 8 if the student graduated but if not it will show how far the student advanced in their studies before dropping out. Graduated_On_Time is either true or false, true if the student shows up with Semester_Registered 8 exactly 4 years after First_Semester year, such as a student who graduated in started their freshman year in 2014_1 and graduated at the end of 2018_2. Graduated_Late is also true or false, and is true if the student reached Semester_Registered 8 at some point after 4 years after their First_Semester year. Major_ID_End shows the last registered Major_ID for the last semester that the given Student_ID shows up in the list of registered students, and is useful to compare with Major_ID_Start to see if the student changed majors. Age_End and Marital_Status_End registered their respective values in the time period of Final_Semester_Time. Still_Enrolled is true or false, and it is true if the Student_ID is still present in the latest time period worksheet, at present this would be 2019_1 but it would be ideal to have this update in the future to use the latest time period sheet included in the data (since, for example, in a few months we will put in new data which will include 2019_2).
Second, I want a table simply showing Student_ID of students who are no longer registered in the latest time period. This would have column headers as follows:
First_Semester | Student_ID | Major_ID_Start | Gender | Age_Start | Marital_Status_Start | Final_Semester_Time | Final_Semester_Registered | Graduated_On_Time | Graduated_Late | Dropped_Out | Major_ID_End | Age_End | Marital_Status_End
The columns are the same as the other example, except for Dropped_Out, which is true or false and it is true if the student has a Final_Semester_Registered less than 8. The key point here is that this table should only include those Student_IDs where Still_Enrolled is false, and serves as a consolidated list of all of the students who used to be enrolled in the university but are no longer enrolled, allowing for analysis between those who graduated on time, those who graduated late, and those who dropped out.
I have achieved some of these results using Excel, but it is a drawn out and manual process, which must be re-done every time the data file updates. Excel has also become fairly slow in loading the file and updating the formula calculations, so I would like to move this to the r statistical software. For reference, though, here are some of the formulas I used in Excel, to give an idea of what might be adaptable into r.
I have a consolidated table with each Student_ID as a row and it includes columns like:
Student_ID | Major_ID_2014_1 | Major_ID_2014_2 | Major_ID_2015_1 | Semester_Registered_2014_1 | Semester_Registered_2014_2 | Semester_Registered_2014_2 | Final_Semester_Time | Final_Semester_Registered | Age_Start | Age_End
This is abbreviated, since it includes both Major_Id and Semester_Registered columns from 2014_1 up through 2019_1, but here in my example I a just showing up to 2015_1 to give the idea.
The formula for Major_ID_2041_1 is =IFERROR(INDEX(Semester_2014_1,MATCH(Student_ID_Cell,Student_IDs_2014_1,0)),"") where Semester_2014_1 and Student_IDs_2014_1 are named ranges from the worksheet of the time period 2014_1 including the relevant rows. A similar formula uses a different named data set for the rows related to Semester_Registered. Then I can use something like =IF(SUMPRODUCT(1/COUNTIF(F3:R3,F3:R3))<3,FALSE,TRUE) on the range of cells for Major_ID from 2014_1 to 2019_1 (each in its own column) to see if the Major_ID changed (meaning the student changed majors or changed campuses) and I can use a MAX() formula for the range of columnns for Semester_Registered to find the highest semester the student reached. A formula like =LOOKUP(2,1/(V3:AH3<>""),$V$2:$AH$2) which goes over the same range of columns for Seester_Registered where the second row has a header like 2014_1, 2014_2, etc. returns the last column that is not blank (thus the last column the student was registered in). This can then be used with the an INDIRECT() in order to reference a named data set (I had to manually name all the data sets in each worksheet by time period) like =IFERROR(INDEX(INDIRECT(CONCATENATE("DATA_",AK3)),MATCH(T3,INDIRECT(CONCATENATE("Student_IDs_",AK3)),0),4),"") where AK3 contains the Final_Semester_Time, like 2014_1.
Basic requirements:
I have a table with a bunch of attributes (20-30), but only 3 are used in querying: User, Category, and Date, and would be structured something like this...
User | Category | Date | ...
1 | Red | 5/15
1 | Green | 5/15
1 | Red | 5/16
1 | Green | 5/16
2 | Red | 5/18
2 | Green | 5/18
I want to be able to query this table in the following 2 ways:
Most recent rows (based on Date) by User. e.g., User=1 returns the 2 rows from 5/16 (3rd and 4th row)
Most recent rows (based on Date) by User and Category. e.g., User=1, Category=Red returns the 5/16 row only (the 3rd row).
Is the best way to model this with a HASH on User, RANGE on Date, and a GSI with HASH on User+Category and RANGE on Date? Is there anything else that might be more efficient? If that's the path of least resistance, I'd still need to know how many rows to return, which would require doing a count against distinct categories or something?
I've decided that it's going to be easier to just change the way I'm storing the documents. I'll move the category and other attributes into a sub-document so I can easily query against User+Date and I'll do any User+Category+Date querying with some client-side code against the User+Date result set.
I have two tables that are linked via a relation (edit -> data table properties -> relations). One contains some raw data, and the other contains aggregated data (calculation on the value).
You can see some examples below. Here, data are linked on "category" column.
RAW DATA
category | id | value
---------+----+------
A | 1 | 10
A | 2 | 20
A | 3 | 30
A | 4 | 30
B | 1 | 20
B | 2 | 20
COMPUTED DATA
category | any_calculation //aggregation of raw data based on category
---------+----------------
A | 10
B | 20
To do the calculation, I use a R/TERR function that take raw data as an input, and that output computed data.
Then I display raw data in a scatter plot (one per category), and I add a curve that is taken from the column "any_calculation" of the computed data.
My main problem is that my table with computed data isn't filled by the R/TERR script. The cause is, in my opinion, the cyclic dependency between those two tables.
Do you have any idea/workaround/fix ?
I should also add that I can't do the calculation in the scatter plot (huge calculation). I use Spotfire 7.8.0.
It seems like a table can't be modified/edited by different sources, that is to say multiple scripts (R and Python) can't have the same table as an output.
To fix my problem, I created a new table in one of my script. Then I created a relation between this table and the other one from the other script.
In my SQLite database I have a table called Tracks which is made up of the following columns: artist, track, genre1, genre2, genre3.
The table contains many values which have the same artist and track values with different genre1, genre2, genre3 values. Like the example below:
artist | track | genre1 | genre2 | genre3
ABBA | Song 1 | Rock | Rock | Rock
U2 | Song 4 | Rock | Rock | Rock
ABBA | Song 1 | Pop | Pop | Pop
U2 | Song 4 | Pop | Pop | Pop
ABBA | Song 1 | 70s | 70s | 70s
U2 | Song 4 | 90s | 90s | 90s
I need to create an SQLite statement that will amalgamate all unique genre values where the artist and track are the same, like the example shown below:
artist | track | genre1 | genre2 | genre3
ABBA | Song 1 | Rock | Pop | 70s
U2 | Song 4 | Pop | Rock | 90s
Any help with this would be hugely appreciated.
Unfortunately it looks like you are suffering from poor database design. In the design above you are limited to only 3 genre per song, and when only one genre is applicable to a song you just repeat the same value across all genre fields. In a good design you should be able to have as many or as little genre entries per song, as well you can also reduce the amount of data required to store genre values.
So lets talk many-to-many relations and mapping tables. A many-to-many relationship in this situation would be many unique genre values belonging to many unique song entries. This should also be true for the reverse, because we will also have many unique songs belonging to many (or possibly none) genres.
So how do we achieve this... Well first lets make a table called Genres. I should have two fields: a integer primary key, and a string for the genre value, with a unique constraint on the latter. Next we will need a mapping table (for futer reference lets call it SongGenre), a table that just has two foreign key fields, one for referencing the song table and one for referencing the genre table. Each record will say this genre belongs to this song, and we can have multiple entries per song.
once you get your mapping table setup you can achieve what you want to do with something like this
SELECT Artist, Track, Group_Concat(Genre.GenreName)
FROM Song
JOIN SongGenre USING (SongID)
JOIN Genre USING (GenreID)
GROUP BY Artist, Track;