How to get id's from SQLite db into pandas df where the values match but not the data types

Viewed 89

Problem

I get a dataframe with a datetime information, need to extract date and time, store both in a database, get the id's back and replace the date/time values with the database id's.

This should be a very common problem, but I couldn't find a solution.

Preconditions

I receive a dataframe from an API like this (with data between a few 100 to >100000 rows):

data = {
        'datetime' : [
            '2022-02-23 10:00:00',
            '2022-02-23 10:05:00',
            '2022-02-23 10:10:00',
            '2022-02-23 10:20:30',
            '2022-02-23 10:42:00'],
        'temp' : [
            16.539,
            16.98,
            16.82001,
            17.03,
            17.85]
        }
df = pd.DataFrame(data)

The data types of the columns are:

print(df.dtypes)
datetime     object
temp        float64

The data has to be stored in a SQLite database. There is a table for time, date and measured data like this:

CREATE TABLE Tempvalues (
            id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
            date_id INTEGER NOT NULL,
            time_id INTEGER NOT NULL,
            tval REAL
            );

CREATE TABLE Time (
            id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
            time TEXT NOT NULL UNIQUE
            );

CREATE TABLE Date (
            id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
            date TEXT NOT NULL UNIQUE
            );

This is the way I extract date and time into seperate columns in the dataframe:

df['date'] = pd.to_datetime(df['datetime']).dt.date
df['time'] = pd.to_datetime(df['datetime']).dt.time

Result:

              datetime      temp        date      time
0  2022-02-23 10:00:00  16.53900  2022-02-23  10:00:00
1  2022-02-23 10:05:00  16.98000  2022-02-23  10:05:00
2  2022-02-23 10:10:00  16.82001  2022-02-23  10:10:00
3  2022-02-23 10:20:30  17.03000  2022-02-23  10:20:30
4  2022-02-23 10:42:00  17.85000  2022-02-23  10:42:00

Data types:

print(df.dtypes)
datetime     object
temp        float64
date         object
time         object
dtype: object      

Store the 'date' and 'time' column in the database:

df[['date', 'time']].to_sql('Tempdf', conn, index=False)

Update the Time and Data tables and remove Tempdf table:

with conn:
    cur.execute("UPDATE Tempdf SET time=STRFTIME('%H:%M:%S', time);")
    cur.execute("INSERT OR IGNORE INTO Date (date) SELECT date FROM Tempdf;")
    cur.execute("INSERT OR IGNORE INTO Time (time) SELECT time FROM Tempdf;")
    cur.execute("DROP TABLE IF EXISTS Tempdf;")

Replace 'date', 'time' with id / what I tried

Up to this point, everything works fine. Now I want to replace the date and time values in the dataframe with the Date.id and Time.id values from the database. Since I shouldn't iterate over a pandas dataframe, I thought using the pandas replace or map methods for this task would be a good idea. But it fails because the data types don't match:

date_date_id = dict(cur.execute("SELECT date, id FROM Date").fetchall())
time_time_id = dict(cur.execute("SELECT time, id FROM Time").fetchall())

I get back a string and an integer:

print(date_date_id)
{'2022-02-23': 1}

Using pandas map method:

df['date'] = df['date'].map(date_date_id)
df['time'] = df['time'].map(time_time_id)

Result:

print(df)
              datetime      temp  date  time
0  2022-02-23 10:00:00  16.53900   NaN   NaN
1  2022-02-23 10:05:00  16.98000   NaN   NaN
2  2022-02-23 10:10:00  16.82001   NaN   NaN
3  2022-02-23 10:20:30  17.03000   NaN   NaN
4  2022-02-23 10:42:00  17.85000   NaN   NaN

Data types:

datetime     object
temp        float64
date        float64
time        float64
dtype: object    

When I try this with floats, it works like expected:

test_temp = {16.539 : 42, 16.98 : 42}
df['temp'] = df['temp'].map(test_temp)

Result:

              datetime  temp        date      time
0  2022-02-23 10:00:00  42.0  2022-02-23  10:00:00
1  2022-02-23 10:05:00  42.0  2022-02-23  10:05:00
2  2022-02-23 10:10:00   NaN  2022-02-23  10:10:00
3  2022-02-23 10:20:30   NaN  2022-02-23  10:20:30
4  2022-02-23 10:42:00   NaN  2022-02-23  10:42:00

So I assume it doesn't replace the values because the data type of 'date' and 'time' is object instead of string.

The result is the same, when the replacement value is pointing to an empty column:

df['date_id'] = df['date'].map(date_date_id)
df['time_id'] = df['time'].map(time_time_id)

Result:

              datetime      temp        date      time  date_id  time_id
0  2022-02-23 10:00:00  16.53900  2022-02-23  10:00:00      NaN      NaN
1  2022-02-23 10:05:00  16.98000  2022-02-23  10:05:00      NaN      NaN
2  2022-02-23 10:10:00  16.82001  2022-02-23  10:10:00      NaN      NaN
3  2022-02-23 10:20:30  17.03000  2022-02-23  10:20:30      NaN      NaN
4  2022-02-23 10:42:00  17.85000  2022-02-23  10:42:00      NaN      NaN

It feels like this is a task that should be completely done in sql, but I failed to do so. This inserts the same id in every row, probably the first one that matches:

WITH tid (t_id, t_time, df_time) AS
    (SELECT Time.id, Time.time, Tempdf.time
     FROM Tempdf
     JOIN Time
        ON Time.time=Tempdf.time)

UPDATE Tempdf
SET time_id = (SELECT t_id FROM tid WHERE t_time = df_time);

Questions

  • How do I get the Date.id and Time.id values into the dataframe, where it matches the values of the 'date'/'time' columns? -> partially answered

  • Do I need to change the data type somehow from object to string? If yes, how? -> answered

  • Is this a proper way to replace the values in the dataframe with the database id's or am I on the wrong path?

  • Is there a native SQLite solution?

Sorry for beeing a bit lost on this. I'd be very grateful for any help.

1 Answers

With a change of the data type to string for the columns 'date' and 'time', the map method works.

df['date'] = df['date'].astype('str').map(date_date_id)
df['time'] = df['time'].astype('str').map(time_time_id)

But I would still appreciate answers to the other questions.

Related