python - seleccionar - funciones de pandas para leer datos a tablas
La mejor manera de unirse/fusionarse por rango en pandas (5)
Considere que su marco de datos A es
A = pd.DataFrame([[0,2],[1,3],[2,4],[3,5],[4,6]],columns=[''A_id'', ''A_value''])
y el marco de datos B es
B = pd.DataFrame([[0,1,2,''a''],[1,4,9,''b''],[2,2,5,''c''],[3,6,7,''d''],[4,8,9,''e'']],columns=[''B_id'', ''B_low'', ''B_high'', ''B_name''])
usando esto a continuación obtendrá la salida deseada
A = A[(A[''A_value'']>=B[''B_low''])&(A[''A_value'']<=B[''B_high''])]
Con frecuencia uso pandas para fusionar (unir) usando una condición de rango.
Por ejemplo, si hay 2 marcos de datos:
A (A_id, A_value)
B (B_id, B_low, B_high, B_name)
que son grandes y aproximadamente del mismo tamaño (digamos que 2M registra cada uno).
Me gustaría hacer una unión interna entre A y B, por lo que A_value estaría entre B_low y B_high.
Usando la sintaxis SQL que sería:
SELECT *
FROM A,B
WHERE A_value between B_low and B_high
y eso sería realmente fácil, corto y eficiente.
Mientras tanto, en los pandas, la única forma (que no está usando los bucles que encontré) es creando una columna ficticia en ambas tablas, unir en ella (equivalente a la unión cruzada) y luego filtrar las filas innecesarias. Eso suena pesado y complejo:
A[''dummy''] = 1
B[''dummy''] = 1
Temp = pd.merge(A,B,on=''dummy'')
Result = Temp[Temp.A_value.between(Temp.B_low,Temp.B_high)]
Otra solución que tuve es aplicar a cada uno de los valores de A una función de búsqueda en B usando la máscara
B[(x>=B.B_low) & (x<=B.B_high)]
, pero también suena ineficiente y podría requieren optimización de índice.
¿Existe una forma más elegante y / o eficiente de realizar esta acción?
No estoy seguro de que sea más eficiente, sin embargo, puede usar sql directamente (desde el módulo sqlite3, por ejemplo) con pandas (inspirados en esta pregunta ) como:
conn = sqlite3.connect(":memory:")
df2 = pd.DataFrame(np.random.randn(10, 5), columns=["col1", "col2", "col3", "col4", "col5"])
df1 = pd.DataFrame(np.random.randn(10, 5), columns=["col1", "col2", "col3", "col4", "col5"])
df1.to_sql("df1", conn, index=False)
df2.to_sql("df2", conn, index=False)
qry = "SELECT * FROM df1, df2 WHERE df1.col1 > 0 and df1.col1<0.5"
tt = pd.read_sql_query(qry,conn)
Puede adaptar la consulta según sea necesario en su aplicación
No sé qué tan eficiente es, pero alguien escribió un contenedor que le permite usar la sintaxis SQL con objetos pandas. Eso se llama pandasql . La documentación indica explícitamente que las uniones son compatibles. Esto podría ser al menos más fácil de leer ya que la sintaxis SQL es muy legible.
Tomemos un ejemplo simple:
df=pd.DataFrame([2,3,4,5,6],columns=[''A''])
devoluciones
A
0 2
1 3
2 4
3 5
4 6
ahora definamos un segundo marco de datos
df2=pd.DataFrame([1,6,2,3,5],columns=[''B_low''])
df2[''B_high'']=[2,8,4,6,6]
resultados en
B_low B_high
0 1 2
1 6 8
2 2 4
3 3 6
4 5 6
aquí vamos; y queremos que la salida sea el índice 3 y el valor A 5
df.where(df[''A'']>=df2[''B_low'']).where(df[''A'']<df2[''B_high'']).dropna()
resultados en
A
3 5.0
Preparar
Considere los marcos de datos
A
y
B
A = pd.DataFrame(dict(
A_id=range(10),
A_value=range(5, 105, 10)
))
B = pd.DataFrame(dict(
B_id=range(5),
B_low=[0, 30, 30, 46, 84],
B_high=[10, 40, 50, 54, 84]
))
A
A_id A_value
0 0 5
1 1 15
2 2 25
3 3 35
4 4 45
5 5 55
6 6 65
7 7 75
8 8 85
9 9 95
B
B_high B_id B_low
0 10 0 0
1 40 1 30
2 50 2 30
3 54 3 46
4 84 4 84
numpy
La forma más
fácil
es utilizar transmisiones con
numpy
.
Buscamos que cada instancia de
A_value
sea mayor o igual que
B_low
mientras que al mismo tiempo
A_value
es menor o igual que
B_high
.
a = A.A_value.values
bh = B.B_high.values
bl = B.B_low.values
i, j = np.where((a[:, None] >= bl) & (a[:, None] <= bh))
pd.DataFrame(
np.column_stack([A.values[i], B.values[j]]),
columns=A.columns.append(B.columns)
)
A_id A_value B_high B_id B_low
0 0 5 10 0 0
1 3 35 40 1 30
2 3 35 50 2 30
3 4 45 50 2 30
Para abordar los comentarios y dar algo parecido a una unión izquierda, agregué la parte de
A
que no coincide.
pd.DataFrame(
np.column_stack([A.values[i], B.values[j]]),
columns=A.columns.append(B.columns)
).append(
A[~np.in1d(np.arange(len(A)), np.unique(i))],
ignore_index=True, sort=False
)
A_id A_value B_id B_low B_high
0 0 5 0.0 0.0 10.0
1 3 35 1.0 30.0 40.0
2 3 35 2.0 30.0 50.0
3 4 45 2.0 30.0 50.0
4 1 15 NaN NaN NaN
5 2 25 NaN NaN NaN
6 5 55 NaN NaN NaN
7 6 65 NaN NaN NaN
8 7 75 NaN NaN NaN
9 8 85 NaN NaN NaN
10 9 95 NaN NaN NaN