我正在比较两个数据集,以查找某些列上的重复条目。
我首先使用下面的PROC SQL命令(我认为是真正的结果)在SAS中使用以下查询完成了这项工作:
proc sql;
CREATE TABLE t1 AS
SELECT a.*, b.*
FROM
local AS a INNER JOIN neighbor AS b
ON a.surname1 = b.surname2
AND a.yob1 = b.yob2
AND a.cob1 = b.cob2;
quit;
我将此结果输出到csv,提供output_sas.csv
我也使用SQLite3使用相同的查询在Python中完成了这个:
conn = sqlite3.connect(file_path + db_name)
cur = conn.cursor()
cur.execute("""
CREATE TABLE t1 AS
SELECT a.*, b.*
FROM
local AS a INNER JOIN neighbor AS b
ON a.surname1 = b.surname2
AND a.yob1 = b.yob2
AND a.cob1 = b.cob2
""")
我将此输出到csv,提供output_python.csv
。
输出应该相同但不是:
output_sas.csv
包含 123条记录而不是output_python.csv
。
在SAS输出文件中,有123条记录在""
和yob1
列中包含空格yob2
,例如,{{1}中的123条记录看起来像这个样本:
sas_data.csv
我发现这种差异是由yob1 yob2 cob1 cob2 surname1 surname2
"" "" 1 1 xx xx
"" "" 2 2 yy yy
.
.
.
# Continues for 123 records
和yob1
列引起的,在上面的123条记录中包含空格。 yob2
文件中缺少这123条记录对。
[注意:在这项工作中,长度为零的字符串对应于缺失值]
SAS中的PROC SQL例程正在评估空格是否相等,即 output_python.csv
。
Python SQLite代码似乎正好相反,即 "" == "" -> TRUE
即使Python中有"" == "" ->
FALSE
,也会发生这种情况。
为什么会出现这种情况,我需要更改哪些内容才能将SQLite输出与PROC SQL输出相匹配?
注意:两个例程都使用相同的输入数据集。它们完全相同,我甚至手动修改Python代码,以确保列"" == "" -> True
和yob1
包含yob2
的缺失值。
目前,我的SAS PROC SQL代码使用""
,名为data1.sas7bdat
和local
,名为data2.sas7bdat
。
要在Python中使用相同的数据集,在SAS中,我将这些数据集导出到csv并读入Python。
如果我这样做:
neighbor
Pandas将缺失值转换为import pandas as pd
# read in
dflocal = pd.read_csv(csv_path_local, index_col=False)
dfneighbor = pd.read_csv(csv_path_neighbor, index_col=False)
。我们可以使用nan
来查找每列中的nan值的数量:
isnull()
要解决空值问题,我可以通过运行显式将# find null / nan values in yob1 and yob2 in each dataset
len(dflocal.loc[dflocal.yob1.isnull()])
78
len(dfneighbor.loc[dfneighbor.yob2.isnull()])
184
转换为长度为零nan
的字符串:
""
我们可以通过测试已知的dflocal['yob1'].fillna(value="", axis=0, inplace=True)
dfneighbor['yob2'].fillna(value="", axis=0, inplace=True)
:
nan
所以它们是一个长度为0的字符串。
然后通过以下方式将这些内容读入SQL:
dflocal.iloc[393].yob1
`""`
type(dflocal.iloc[393].yob1)
str
然后执行相同的SQLite3代码:
dflocal.to_sql('local', con=conn, flavor='sqlite', if_exists='replace', index=False)
dfneighbor.to_sql('neighbor', con=conn, flavor='sqlite', if_exists='replace', index=False)
即使我做了这个明确的更改 我仍然 获得相同的缺失123值,即使空值已更改为长度为零的字符串{ {1}}。
潜在解决方案:
但是,如果我改为使用conn = sqlite3.connect(file_path + db_name)
cur = conn.cursor()
cur.execute("""
CREATE TABLE t1 AS
SELECT a.*, b.*
FROM
local AS a INNER JOIN neighbor AS b
ON a.surname1 = b.surname2
AND a.yob1 = b.yob2
AND a.cob1 = b.cob2
""")
参数导入数据集,则会为我执行从""
到na_filter=False
的转换。
null
当我将这些数据集导入我的数据库并通过相同的SQL代码运行时:
""
HOORAY我将相同的输出作为SAS代码!
但为什么第一个解决方案不起作用?我在两种情况下都做了同样的事情(第一次使用dflocal = pd.read_csv(csv_path_local, index_col=False, na_filter=False)
dfneighbor = pd.read_csv(csv_path_neighbor, index_col=False, na_filter=False")
# find null / nan values in yob1 and yob2 in each dataset
len(dflocal.loc[dflocal.yob1.isnull()])
0
len(dfneighbor.loc[dfneighbor.yob2.isnull()])
0
手动执行,第二次使用conn = sqlite3.connect(file_path + db_name)
cur = conn.cursor()
cur.execute("""
CREATE TABLE t1 AS
SELECT a.*, b.*
FROM
local AS a INNER JOIN neighbor AS b
ON a.surname1 = b.surname2
AND a.yob1 = b.yob2
AND a.cob1 = b.cob2
""")
)。
答案 0 :(得分:3)
在SAS中,字符的空值概念并不存在。它更像是一个空字符串。 但是,在大多数SQL实现中(包括SQlite,我假设),空值和空字符串将是不同的。
SAS中的空白值确实评估为"" = ""
true
然而,在您的平均DBMS中,您会称之为空白值'通常是null
值,而不是空字符串(""
)。 null=null
不正确。您无法将空值与任何内容进行比较,包括空值。
您可以做的是将您的SQlite更改为
CREATE TABLE t1 AS
SELECT a.*, b.*
FROM
local AS a INNER JOIN neighbor AS b
ON a.surname1 = b.surname2
AND coalesce(a.yob1,'') = coalesce(b.yob2,'')
AND a.cob1 = b.cob2
当yob
为空时,coalesce函数将用空字符串替换yob
。
请注意,如果yob1
为空并且yob2
实际上是空字符串,添加这些合并函数将改变null=''
条件,这不是真的,到''=''
这是真的。
如果这不是你想要的,你也可以这样写:
CREATE TABLE t1 AS
SELECT a.*, b.*
FROM
local AS a INNER JOIN neighbor AS b
ON a.surname1 = b.surname2
AND (a.yob1 = b.yob2
OR (a.yob1 is null AND a.yob2 is null)
)
AND a.cob1 = b.cob2
答案 1 :(得分:2)
听起来像是被SQLite3(和大多数DBMS)处理空值的方式所击中。在SAS中,您可以将空值与实际值进行比较,但在大多数DBMS系统中,您不能。因此,在SAS中,诸如(A = B)和(A ne B)的互补逻辑比较将总是产生一个为真,另一个为假。但是在DBMS中,当A或B或两者都为NULL时,则(A = B)和(A ne B)都将为FALSE。 NULL值既不小于也不大于另一个值。
在SAS中如果两个值都为NULL,那么它们是相等的,一个是NULL而另一个不是那么它们不相等。 NULL数值小于任何实际数字。 NULL字符变量不存在,因此只被视为空白填充值。请注意,SAS在比较字符变量时也会忽略尾随空白。
这在实践中意味着您在查询DBMS时需要添加额外的代码来处理NULL值。
ON (a.surname1 = b.surname2 or (a.surname1 is null and b.surname1 is null))
AND (a.yob1 = b.yob2 or (a.yob1 is null and b.yob2 is null))
AND (a.cob1 = b.cob2 or (a.cob1 is null and b.cob2 is null))